Query a database from JavaScript is easy using Axional library. When performing a query,
database returns an SQL ResultSet
.
A SQL result set is a set of rows from a database, as well as metadata about the query such as the column names, and the types and sizes of each column. Depending on the database system, the number of rows in the result set may or may not be known. Usually, this number is not known up front because the result set is built on-the-fly.
A result set is effectively a table. The ORDER BY clause can be used in a query to impose a certain sort condition on the rows. Without that clause, there is no guarantee whatsoever on the order in which the rows are returned.
Specific wrappers around database ResultSet
and in memory ResultSet
are provided
as built-in Javascript objects. You can read more about how to handle ResultSet
objects in the Ax.rs
package.
The database object provides basic methods to perform query execution.
Return | Method | Description |
---|---|---|
JSResultSet
|
executeQuery(String sql, Object... args) | Executes a query. |
Object | executeGet(String sql, Object... args) | Executes a single row and single column query. Better use executeGetXXX for the appropiate number type to avoid script engine side effects. |
int | executeGetInt(String sql, Object... args) | Executes a single row and single column query |
long | executeGetLong(String sql, Object... args) | Executes a single row and single column query |
double | executeGetDouble(String sql, Object... args) | Executes a single row and single column query |
JSResultSet
|
executeScriptOrQuery(String sql, Object... args) | Executes a script or query |
JSSQLCA
|
execute(String sql, Object... args) | Execute an SQL statement that may return a ResultSet or not depending statement type |
1 Writing a statement
You can write a statement as literal string is a sequence of bytes or characters, enclosed within either two single quotes (' ') or two double quotes (" ").
1.1 Passing arguments
All methods admit variable number of arguments to match SQL statement place holders.
<script> var rs = Ax.db.executeQuery( "SELECT tabname, created FROM systables WHERE tabid = ?", 1 ); console.log(rs); </script>
+----------------------------------------+----------+
|tabname |created |
+----------------------------------------+----------+
|systables |2018-05-17|
+----------------------------------------+----------+
1.2 Using template literals
Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them. They were called "template strings" in prior editions of the ES2015 specification.
Template literals are enclosed by the back-tick (` `) (grave accent
) character instead of double or single quotes.
Template literals can contain placeholders.
These are indicated by the dollar sign and curly braces (${expression}
).
The expressions in the placeholders and the text between them get passed to a function.
<script> var tabid = 1; var rs = Ax.db.executeQuery( `SELECT tabname, created FROM systables WHERE tabid = ${tabid}` ); console.log(rs); </script>
+----------------------------------------+----------+
|tabname |created |
+----------------------------------------+----------+
|systables |2018-05-17|
+----------------------------------------+----------+
1.3 Multiline strings
Any newline characters inserted in the source are part of the template literal. This has a great advantage for readability when writing large multi line SQL statements.
<script> var tabid = 1; var rs = Ax.db.executeQuery(` SELECT tabname, created FROM systables WHERE tabid = ${tabid} ` ); console.log(rs); </script>
2 Quering a database
You have several methods to query a database witch are more simple than their Java synonyms.
2.1 executeQuery
The executeQuery statements executes the SQL query in this PreparedStatement object and returns the ResultSet
object generated by the query.
<script> return Ax.db.executeQuery( "SELECT tabid, tabname, nrows, created FROM systables WHERE tabname='systables'" ); </script>
data
+----------+----------------------------------------+----------------+----------+
|tabid |tabname |nrows |created |
|serial |varchar |float |date |
+----------+----------------------------------------+----------------+----------+
| 1|systables | 89.000|2018-04-08|
+----------+----------------------------------------+----------------+----------+
2.1.1 Iterating results
The ResulSet object is iterable. So we can use a for loop to traverse it. On each iteration, the returned object is a JSON object with row data.
<script> var rs = Ax.db.executeQuery( "SELECT tabid, tabname, nrows, created FROM systables WHERE tabname='systables'" ); for (var row of rs) { console.log(row.tabid + "," + row.tabname); } </script>
data
1,systables
2.1.2 Get current Row Number in a Resultset iteration
The ResulSet object is iterable. In each iteration an internal row counter is incremente, so you can know how many rows are you currently retrieved from the Resultset. You can access this counter by callint getRow() method..
<script> const rs = Ax.db.executeQuery(` <select first='10'> <columns>tabname</columns> <from table='systables'/> </select>`); var currentRow = 0; for (var systables of rs) { console.log("ROW = " + rs.getRow() + " TABNAME=" + systables.tabname); } </script>
Console result
ROW = 1 TABNAME=systables
ROW = 2 TABNAME=syscolumns
ROW = 3 TABNAME=sysindices
ROW = 4 TABNAME=systabauth
ROW = 5 TABNAME=syscolauth
ROW = 6 TABNAME=sysviews
ROW = 7 TABNAME=sysusers
ROW = 8 TABNAME=sysdepend
ROW = 9 TABNAME=syssynonyms
ROW = 10 TABNAME=syssyntable
2.2 executeGet
The method executeGet
is simplified query for single row / column values. For example,
to get the count(*) of some table we have several options.
-
Using executeQuery and iterating to first row to convert it to JSON.
Copy<script> var rs = Ax.db.executeQuery("SELECT COUNT(*) as count FROM systables"); var count; for (var row of rs) { count = row.count; } console.log(count); </script>
88
-
Using executeQuery and toOne function to convert directly to a JSON object.
Copy<script> var row = Ax.db.executeQuery("SELECT COUNT(*) as count FROM systables").toOne(); count = row.count; console.log(count); </script>
88
-
Using the
executeGet
method is quite simple.Copy<script> var count = Ax.db.executeGet("SELECT COUNT(*) FROM systables"); console.log(count); </script>
88
Notice that in methods 1 and 2 we need to provide an alias for column name.
2.2.1 The data type returned
executeGet
returns the native databse type and may cause some
undesired side effects. In some databases (Informix for example) the
COUNT(*) returns a BigDecimal. This may cause a fail when comparing
the returned value with a given value depending the script engine.
The problem may be related when the value returned is BigDecimal or BigInteger. Nashorn compares a BigDecimal or BigInteger with a javascript number but Graal will not.
Nashorn script engine
<script type="nashorn"> var count = Ax.db.executeGet("SELECT COUNT(*) FROM systables WHERE tabid = -1"); console.log(count); console.log(count == 0); </script>
0
true
Graal script engine
<script type="graal"> var count = Ax.db.executeGet("SELECT COUNT(*) FROM systables WHERE tabid = -1"); console.log(count); console.log(count == 0); </script>
0
false
Use executeGetInt, executeGetLong or executegetDouble to get the appropiate data type to avoid script engine side effects.
2.3 executeScriptOrQuery
The executeScriptOrQuery statements executes the XSQL code in this PreparedStatement object and returns one ResultSet
object.
-
Calling one xsql script without named columns.
Copy<script> var message = "Hello world"; const rsXsql = Ax.db.executeScriptOrQuery(` <call> <args> <arg>${message}</arg> </args> <![CDATA[ <xsql-script> <args> <arg name='param1' type='string' /> </args> <body> <return> <param1 /> </return> </body> </xsql-script>]]> </call> `); // Get return data for (const row of rsXsql) { console.log(row['(constant_1)']); } </script>
{(constant_1)=Hello world} Hello world
-
Calling one xsql script with named columns.
Copy<script> const rsXsql = Ax.db.executeScriptOrQuery(` <call into='col1'> <args> <arg>Hello world</arg> </args> <![CDATA[ <xsql-script> <args> <arg name='param1' type='string' /> </args> <body> <return> <param1 /> </return> </body> </xsql-script>]]> </call> `); // Get return data for (const row of rsXsql) { console.log(row.col1); } </script>
{col1=Hello world} Hello world
3 Executing statements
The execute method executes the SQL statement in a PreparedStatement object, which may be any kind of SQL statement. Some prepared statements return multiple results; the execute method handles these complex statements as well as the simpler form of statements handled by the methods executeQuery.
The execute statement returns a SQLCA
object.
3.1 SQLCA object
SQLCA
is called as a SQL communication Area. SQLCA
will have all the information like return code, error id,
error details, serial information, etc after database statement execution.
3.1.1 Rows affected count
The count
property stores the number of rows affected by an INSERT, DELETE or UPDATE operation. This information can also be
retrieved by calling the getCount()
method on SQLCA
class.
<script> Ax.db.execute("CREATE TABLE IF NOT EXISTS test1 (a VARCHAR(3), b SMALLINT)"); Ax.db.execute("INSERT INTO test1 VALUES('a', 1)").getCount(); Ax.db.execute("INSERT INTO test1 VALUES('b', 2)").getCount(); Ax.db.execute("INSERT INTO test1 VALUES('c', 3)").getCount(); var updated = Ax.db.execute("UPDATE test1 SET b = b + 1").getCount(); console.log(updated + " rows updated"); </script>
3 rows updated
3.1.2 Last serial inserted
The serial
property stores the serial value stored in a serial column from last INSERT executed.
This information can also be retrieved by calling the getSerial()
method on SQLCA
class.
<script> Ax.db.execute("CREATE TABLE IF NOT EXISTS test2 (a SERIAL, b CHAR(3))"); console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'a')").getSerial()); console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'b')").getSerial()); console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'c')").getSerial()); </script>
1
2
3
3.1.3 Time of statement
You can obtain the time in milliseconds of last operation accessing time
member on SQLCA
object.
<script> Ax.db.execute("CREATE TEMP TABLE IF NOT EXISTS test2 (a SERIAL, b CHAR(3))"); console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'a')").time); </script>
0
4 Select statement
The select statement may produce two types of return values:
- A
ResultSet
, when number of rows are undetermined. - A JSON object, when one and only one row is returned.
4.1 Getting a ResultSet
The response for a database query is a SQL ResultSet
is a set of rows, as well as metadata about the query such as the column names,
and the types and sizes of each column.
A ResultSet
is effectively a table. The ORDER BY clause can be used in a query to impose a certain sort condition on the rows.
Without that clause, there is no guarantee whatsoever on the order in which the rows are returned.
The ResultSet
object implements the Iterable interface so it can be interated using Javascript for ... of
statement.
<script> var rs_cars = Ax.db.executeQuery("SELECT * FROM cars"); for (var row of rs_cars) { console.log(row); } </script>
{cylynders=null, acceleration=11.5, seqno=2, horsepower=165, mpg=15, year=70, origin=US, weight=3693, model=buick skylark 320, displacement=350}
{cylynders=null, acceleration=11, seqno=3, horsepower=150, mpg=18, year=70, origin=US, weight=3436, model=plymouth satellite, displacement=318}
{cylynders=null, acceleration=12, seqno=4, horsepower=150, mpg=16, year=70, origin=US, weight=3433, model=amc rebel sst, displacement=304}
4.2 Getting one JSON object
Some times we want a single object so we would like to avoid the need to iterate the ResultSet
to get it.
Usually, using toOne() ,ethod is enough as it returns a ResultSetRowMap
class that is almost similar to a JSON Object,
but if you require a string JSON representation (for example to return it into a REST API), you can convert it by using toJSON() method.
var row = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 2").toOne(); console.log(typeof(row)); console.log(row); var json = row.toJSON(); console.log(typeof(json)); console.log(json);
object
{cylynders=null, acceleration=11.5, seqno=2, horsepower=165, mpg=15, year=70, origin=US, weight=3693, model=buick skylark 320, displacement=350}
string
{cylynders=null, acceleration=11.5, seqno=2, horsepower=165, mpg=15, year=70, origin=US, weight=3693, model=buick skylark 320, displacement=350}
If SQL statement returns more than one row it will fail with an SQLException as only one row is expected.
4.2.1 Ensuring row exists
If no row exists an empty JSON object is returned. But in many cases we would like to stop an application if some required data is missing.
You can use the setRequired(message)
function to fire an exception is no row is found.
<script> var row = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 99").toOne().setRequired("No car found"); </script>
java.sql.SQLException: No car found
4.3 Getting a single value
To get a single column like when we want to select a COUNT(*) we can use a fastest way
avoiding either the need of a ResultSet
nor the JSON object. We will get only a single
value from a single row.
<script> var count = Ax.db.executeGet("SELECT COUNT(*) FROM cars"); console.log("count is " + count); </script>
count is 3
If SQL statement contains more than one column or returns more than one row it will fail with an SQLException.
4.4 Escape strings to prevent SQL Injection
To avoid SQL Injection if you don't use prepared statements and embed variable content directly inside SQL Statement, you should escape this string to avoid SQL Injection.
<script> var str_tabname = "' OR 1=1; --"; var sqlstmt = `SELECT * FROM systables WHERE tabname = '${Ax.db.escape(str_tabname)}'`; var tab = Ax.db.executeQuery(sqlstmt).toOne(); console.log(tab); </script>
{}
5 Database ResultSets
You can perform database querys by using the executeQuery
method. This method returns
a ResultSet
object.
A ResultSet
object maintains a cursor pointing to its current row of data. Initially the cursor
is positioned before the first row. The next method moves the cursor to the next row, and because
it returns false when there are no more rows in the ResultSet
object, it can be used in a while
loop to iterate through the result set.
ResultSet
object is iterable so it can be travesed using the for each
statement.
On each iteration, next row fetched is returned as a JSON object.
5.1 ResultSet close
Database resultsets consume database resources while open. Application will automatically
take care of ResultSet
close when:
- Affer a call to next() with no results, if
ResultSet
is forward only it will be automatically closed. - Affer
ResultSet
iteration, ifResultSet
is forward only it will be automatically closed. - At the end of the script, all
ResultSet
still open will be closed. A warning message will be shown in server console.
Even so, you should better take care of closing a ResultSet
when it's no loger used.
<script> var rs = Ax.db.executeQuery(`SELECT tabname FROM systables`); for (var syst of rs) { console.log("ROW=" + rs.getRow() + " TABNAME=" + syst.tabname); if (rs.getRow() == 3) { // Breaking cursor iteration. // ResultSet is not going to be consumed, so we need to close it explicitely. rs.close(); break; } } </script>
Not closing open resultset statements can also derive into error "Can not re-use statement while cursor open". Ensure you close all resultset statements after using them.
var rs_cvenfach = Ax.db.executeQuery(`select * from cvenfach`); for (var cvenfach of rs_cvenfach) { var rs_cvenfacl = Ax.db.executeQuery(`SELECT * FROM cvenfacl WHERE facidx = ?`, cvenfach.facidx); for (var cvenfacl of rs_cvenfacl) { // Exit on first row to force not consuming all cursor statement break; } // rs_cvenfacl is not fully consumed, so we need to close it explicitly // to avoid "Can not re-use statement while cursor open" error rs_cvenfacl.close(); } rs_cvenfach.close();
5.2 ResultSet holdabiliy
Result set holdability is the ability to specify whether cursors (or a result set as java.sql.ResultSet
objects)
should be held open or closed at the end of a transaction. A holdable cursor, or result set, is one that does not
automatically close when the transaction that contains the cursor is committed.
By default javascript ResultSets are holdable, so they survive a transaction commit or rollback.
5.3 ResultSetRowMap object
When iterating a ResultSet
you will get a map of type ResultSetRowMap
. This object has some
extensions that are very useful.
Return | Method | Description |
---|---|---|
String | toJSON() | Returns the JSON representation of this object. |
boolean | hasChanged() | Returns true if map has been changed. |
ResultSetRowMap
|
getChanged() | Returns a new ResultSetRowMap with values that have changed since last call. |
ResultSetRowMap
|
setNotFound(String msg) | Throws an exception with given message it map contains no data (isNotFound()). Otherwise returns the map itself |
boolean | isNotFound() | Returns true if map contains no data as a result of a query with no results. Map may still contain all column keys but all are null. |
5.3.1 ResultSet iteration
During iteration, the object returned is a map of type ResultSetRowMap
<script> var rs = Ax.db.executeQuery("SELECT tabname, created FROM systables WHERE tabid < 3"); for each (var row in rs) { // row is a JSON object with database row data console.log(row) } </script>
{
"tabname": "systables",
"created": "2020-05-08"
}
{
"tabname": "syscolumns",
"created": "2020-05-08"
}
5.3.2 Returning a single row object
ResultSet
obtained by executing Ax.db.executeQuery provides some methods to obtain a single row or single value from cursor returned.
Depending method used, it can control if only one row is selected if at least one row is selected or return first row selected.
All this functions return an ResultSetRowMap
object with row data and implements isNotFound() method to know if some data has been returned by ResultSet
or not
Return single row
The method toOne()
ensures data contains 0 or 1 row only.
If more that one rows are returned, the statement will fail with an exception.
<script> var m_systables = Ax.db.executeQuery(`SELECT * FROM systables where tabname = ?`, 'systables').toOne(); console.log("notfound:" + m_systables.isNotFound()); var m_systables = Ax.db.executeQuery(`SELECT * FROM systables where tabname = ?`, 'XXX').toOne(); console.log("notfound:" + m_systables.isNotFound()); </script>
notfound:false
notfound:true
Notice that the JavaScript object returned (a Map) contains an special function to determine is row has been found. And that when no data is found, the object contains all columns with null values.
Return first Row
The method firstOne()
returns first row of ResultSet
, whenever number of rows is contained by ResultSet
.
<script> var m_systables = Ax.db.executeQuery(`SELECT * FROM systables where tabname like 'sys%'`).firstOne(); console.log("data:" + m_systables); console.log("notfound:" + m_systables.isNotFound()); </script>
notfound:false
notfound:true
Notice that the JavaScript object returned (a Map) contains an special function to determine is row has been found. And that when no data is found, the object contains all columns with null values.
Automatic fail if notfound
In some cases, we need a database rows exists to continue application. If row
does not exists we should stop application as it's a failure. We can automatically
setup this controls by using setRequired(message)
method.
For example, if you some data is required and application can't continue without it, you can use this method to prevent that situations.
<script> var m_tabname = "UndefinedTable"; // This call will fail with given message if no data is found var m_row = Ax.db .executeQuery('SELECT * FROM systables WHERE tabname = ?', m_tabname) .toOne() .setRequired(`Table ${m_tabname} not found.`); </script>
java.sql.SQLException: Table UndefinedTable not found.
at deister.axional.server.jdbc.resultset.iterator.ResultSetRowMap.setRequired(ResultSetRowMap.java:172)
...
5.3.3 Determine values have changed
You can select a tuple from a database using a ResultSetRowMap
, update some values
and automatically determine the values changed using getChanged()
to generate
a new map with them to be used to perform a database update.
<script> Ax.db.execute("DROP TABLE IF EXISTS t1"); Ax.db.execute("CREATE TABLE t1(a smallint, b varchar(10), c integer)"); Ax.db.execute("INSERT INTO t1 VALUES(1, 'Animal', 5000)"); Ax.db.execute("INSERT INTO t1 VALUES(2, 'Vegetal', 6000)"); var row = Ax.db.executeQuery("SELECT * FROM t1 WHERE a = 1").toOne(); console.log("--- ROW ---"); console.log(row); console.log("--- ROW CHANGED ---"); row["c"] = 9000; console.log(row.hasChanged()); console.log(row.getChanged()); console.log("--- ROW UPDATE ---"); Ax.db.update("t1", row.getChanged(), "a=1"); var rs = Ax.db.executeQuery("SELECT * FROM t1 ORDER BY a"); console.log(rs); rs.close(); </script>
--- ROW ---
{
"a": 1,
"b": "Animal",
"c": 5000
}
--- ROW CHANGED ---
true
{
"c": 9000
}
+--------+-----------+----------+
|t1 |t1 |t1 |
|a |b |c |
|smallint|varchar(10)|int |
+--------+-----------+----------+
|1 |Animal |9000 |
|2 |Vegetal |6000 |
+--------+-----------+----------+
5.4 Getting Rows from ResultSet
The easiest way to get a row from a ResultSet
is to get a ResultsetRowMap object.
5.4.1 Traversing using next
As we have seen, a ResultSet
is converted to ResultsetRowMap on every iteration using toRow
.
<script> var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables"); if (rs.next()) { console.log(rs.toRow()); } rs.close(); </script>
{count=92}
5.4.2 Traversing using iterator
Using an iterator is more easy. The variable row
is a ResultsetRowMap.
<script> var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables"); for (var row of rs) { console.log(row); } </script>
{count=92}
5.4.3 Converting to single row
If we know a query will return only one value we can avoid loop iteration and use direct row to ResultsetRowMap conversion.
<script> var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables"); console.log(rs.toOne()); rs.close(); </script>
{count=92}
6 ResultSet to HTML
To convert a resultset to HTML there are methods:
Return | Method | Description |
---|---|---|
String | {resultset}.toHTML() | Returns a string representation a conversion of resultset to HTML code |
toHTML() method requires rewindable cursor, so you're required to convert resultset to memory resultset before using this method.
function xx() { ... var rs = Ax.db.executeQuery(`SELECT FIRST 2 * FROM systables`).toMemory(); console.log(rs.toHTML(options => { options.setEmitFullHTML(false); //options.setEmitHeadHTML(true); //options.setEmitBodyHTML(true); options.setEmitGlobalStyles(false); options.setEmitGroupStyles(false); })); }
7 ResultSet to JSON
There are multiple methods to convert a ResultSet
to native Javascript representation:
Return | Method | Description |
---|---|---|
String | {resultset}.asJSON() | Returns a string representation of a conversion of the resultset to JSON Object |
Array | {resultset}.toJSONArray() | Returns the resultset as a Native JSON Object (Array of objects). |
Array | {resultset}.toArray() | Returns one Array of Arrays containing resultset row values |
Object | {resultset}.toJSON() | Returns a JSON Object with an internal declaration of resultset structure and data. |
All this methods do not close the resultset. Avoid return rs.asJSON(), rs.toJSONArray(), etc. in functions. Instead, get result into a variable, close resultset and then return variable.
function xx() { ... var result = rs.toJSON(); rs.close(); return result; }
7.1 Convert to ResultSet Representation: toJSON()
To obtain an String with Resultset Representation in JSON, you can use toJSON() method of JSResultSet
Class
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables"); console.log(rs.toJSON()); rs.close();
{
"rows": [
[
1,
"systables"
],
[
2,
"syscolumns"
],
[
3,
"sysindices"
],
],
"cols": [
{
"columnName": "tabid",
"columnType": 4,
"columnIndex": 1,
"precision": 10,
"scale": 0,
"tableName": "systables",
"schemaName": "",
"catalogName": "",
"columnClassName": "java.lang.Integer",
"columnLabel": "tabid",
"columnTypeName": "serial",
"columnDisplaySize": 10,
"isAutoIncrement": true,
"isCaseSensitive": false,
"isCurrency": false,
"isReadOnly": false,
"isSearchable": true,
"isSigned": false,
"isWritable": true,
"isDefinitelyWritable": true,
"isNullable": 0,
},
{
"columnName": "tabname",
"columnType": 12,
"columnIndex": 2,
"precision": 128,
"scale": 0,
"tableName": "systables",
"schemaName": "",
...
}
],
"hasMore": false,
"title": null,
"attributes": null,
"hasMore": false
}
7.2 Convert to String in JSON Representation: asJSON()
Method asJSON() Convert a Resultset to a String with a JSON Object representation.
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables"); var js = rs.asJSON(); console.log(typeof(js); console.log(js); rs.close();
string
[
{
"tabid": 1,
"tabname": "systables"
},
{
"tabid": 2,
"tabname": "syscolumns"
},
{
"tabid": 3,
"tabname": "sysindices"
}
]
7.3 Convert to native JSON Object (Array of objects)
Method toJSONArray() Convert a Resultset to a native JSON Object.
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables"); var js = rs.toJSONArray(); console.log(typeof(js); console.log(js); rs.close();
object
[{
"tabid": 1,
"tabname": "systables"
}, {
"tabid": 2,
"tabname": "syscolumns"
}, {
"tabid": 3,
"tabname": "sysindices"
}]
7.4 Convert to Java (Array of Arrays)
Method toArray() Convert a Resultset to a Java Array Object.
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables"); var js = rs.toArray(); console.log(typeof(js)); console.log(js); for (r = 0; r < js.length; r++) { console.log(`Row ${r}`); for (var c = 0; c < js[r].length; c++) { console.log(js[r][c]); } } rs.close();
object
[[Ljava.lang.Object;@34e04854, [Ljava.lang.Object;@53159100, [Ljava.lang.Object;@4c34c1ac]
Row 0
1
systables
Row 1
2
syscolumns
Row 2
3
sysindices
8 ResultSet to PDF
Every ResultSet
can be converted automatically to Pdf and processed as an stream easily by using toPDF() method.
var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables"); new Ax.io.File("/tmp/resultset.pdf").write(rs.toPDF()); rs.close();
9 ResultSet to XML
<script> var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables"); var xml = "<sqlresponse>\n<rowset>\n"; for (let row of rs) { xml += " <r>\n"; for (obj in row) { xml += " <c>" + row[obj] + "</c>\n"; } xml += " </r>\n"; } xml += "</rowset>\n</sqlresponse>"; console.log(xml); </script>
<sqlresponse>
<rowset>
<r>
<c>1</c>
<c>systables</c>
</r>
<r>
<c>2</c>
<c>syscolumns</c>
</r>
<r>
<c>3</c>
<c>sysindices</c>
</r>
</rowset>
</sqlresponse>
10 ResultSet to CSV
<script> var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables"); var blob = new Ax.sql.Blob("csv_resultset.csv"); new Ax.rs.Writer(rs).csv(options => { options.setResource(blob); }); console.log(blob.getContent()); </script>
tabid,tabname
1,systables
2,syscolumns
3,sysindices
11 Memory ResultSets
You can generate virtual in memory resultsets and return them to client as as database resultset. This kind of resultset are very useful to perform data transformation prior returning them to client applications.
In the following example a ResultSet
with two columns is build and returned to client.
<script> function fibonacci(num) { if (num <= 1) return 1; return fibonacci(num - 1) + fibonacci(num - 2); } var rs = new Ax.rs.Reader().memory(options => { options.setColumnNames([ "number", "fibonacci" ]); options.setColumnTypes([ Ax.sql.Types.INTEGER, Ax.sql.Types.DOUBLE ]); }); for (var idx = 1; idx < 10; idx++) rs.rows().add([idx, fibonacci(idx)]); console.log(rs); // Iterate Memory cursor for each (var row in rs) { console.log(row['number'] + " " + row['fibonacci']); } return rs; </script>
+-------+---------+
|number |fibonacci|
|double |decimal |
+-------+---------+
| 1.000| 1.000|
| 2.000| 2.000|
| 3.000| 3.000|
| 4.000| 5.000|
| 5.000| 8.000|
| 6.000| 13.000|
| 7.000| 21.000|
| 8.000| 34.000|
| 9.000| 55.000|
| 10.000| 89.000|
+-------+---------+
1 1
2 2
3 3
4 5
5 8
6 13
7 21
8 34
9 55
12 Data mapping
12.1 Java to Javascript
Objects returned from database are the correspoding Java objects for each column data type. Javascript will work natively for String, integers and floating point numbers. And will use the Java wrapping for Date, Decimal and Blob data types as they have no corresponding Javascript equivalent.
Java | Javascript |
---|---|
Map | JSON |
List | Array |
Boolean | Native boolean |
Integer | Native number |
Long | Native number |
Float | Native number |
Double | Native number |
String | Native string |
Date | wrapper of Date |
Decimal | wrapper of Decimal |
Blob | wrapper of Blob |
12.2 Javascript to Java
Native objects passed form Javascript to Java are converted to Java types. For example, you can pass a Javascript native date as parameter for database date.
Javascript | Java |
---|---|
JSON object | Map |
Native Array | java.util.List |
Native Date | java.util.Date |
Native Number | java.lang.Double |
Native String | java.lang.String |