Beta Draft: 2016-08-16
Table of Contents
This section explains how to use the X DevAPI for Create Read, Update, and Delete (CRUD) operations.
MySQL's core domain has always been working with relational tables. X DevAPI extends this domain by adding support for CRUD operations that can be run against collections of documents. This section explains how to use these.
CRUD operations are available as methods, which operate on Schema objects. The available Schema objects consist of Collection objects, containing Documents, or Table objects consisting of rows and Collections containing Documents.
The following table shows the available CRUD operations for both Collection and Table objects.
Operation | Document | Relational |
|---|---|---|
Create | ||
Read | ||
Update | ||
Delete |
The X DevAPI supports a number of modern practices to make working with CRUD operations easier and to fit naturally into modern development environments. This section explains how to use method chaining instead of working with SQL strings of JSON structures.
The following examples show how method chaining is used instead of an SQL string when working with XSession and NodeSession. The example assumes that the test schema exists and an employee table exists.
MySQL Shell JavaScript Code
// New method chaining used for executing an SQL SELECT statement
// Recommended way for executing queries
var employees = db.getTable('employee');
var res = employees.select(['name', 'age']).
where('name like :param').
orderBy(['name']).
bind('param', 'm%').execute();
// Traditional SQL execution by passing an SQL string
// This is only available when using a NodeSession
// It should only be used when absolutely necessary
var result = session.sql('SELECT name, age ' +
'FROM employee ' +
'WHERE name like ? ' +
'ORDER BY name').bind('m%').execute();
MySQL Shell Python Code
# New method chaining used for executing an SQL SELECT statement
# Recommended way for executing queries
employees = db.getTable('employee')
res = employees.select(['name', 'age']) \
.where('name like :param') \
.orderBy(['name']) \
.bind('param', 'm%').execute()
# Traditional SQL execution by passing an SQL string
# This is only available when using a NodeSession
# It should only be used when absolutely necessary
result = session.sql('SELECT name, age ' +
'FROM employee ' +
'WHERE name like ? ' +
'ORDER BY name').bind('m%').execute()
Node.js JavaScript Code
// New method chaining used for executing an SQL SELECT statement
// Recommended way for executing queries
var employees = db.getTable('employee');
var promise = employees.select('name', 'age')
.where('name like :name')
.orderBy('name')
.bind('m%').execute();
});
// Traditional SQL execution by passing an SQL string
// This is only available when using a NodeSession
// It should only be used when absolutely necessary
var promise = db.executeSql('SELECT name, age ' +
'FROM employee ' +
'WHERE name like ? ' +
'ORDER BY name').bind('m%').execute();
C# Code
// New method chaining used for executing an SQL SELECT statement
// Recommended way for executing queries
{
var employees = db.GetTable("employee");
var res = employees.Select("name", "age")
.Where("name like :param")
.OrderBy("name")
.Bind("param", "m%").Execute();
// Traditional SQL execution by passing an SQL string
// This is only available when using a NodeSession
// It should only be used when absolutely necessary
var result = session.SQL("SELECT name, age " +
"FROM employee " +
"WHERE name like ? " +
"ORDER BY name").Bind("m%").Execute();
}
Java Code
// New method chaining used for executing an SQL SELECT statement
// Recommended way for executing queries
Table employees = db.getTable("employee");
RowResult res = employees.select("name, age")
.where("name like :param")
.orderBy("name")
.bind("param", "m%").execute();
// Traditional SQL execution by passing an SQL string
// This is only available when using a NodeSession
// It should only be used when absolutely necessary
SqlResult result = session.sql("SELECT name, age " +
"FROM employee " +
"WHERE name like ? " +
"ORDER BY name").bind("m%").execute();
C++ Code
// New method chaining used for executing an SQL SELECT statement
// Recommended way for executing queries
Table employees = db.getTable("employee");
RowResult res = employees.select("name", "age")
.where("name like :param")
.orderBy("name")
.bind("param", "m%").execute();
// Traditional SQL execution by passing an SQL string
// This is only available when using a NodeSession
// It should only be used when absolutely necessary
RowResult result = db.sql("SELECT name, age "
"FROM employee "
"WHERE name like ? "
"ORDER BY name").bind("m%").execute();
Traditionally, many MySQL drivers used a synchronous approach when executing SQL statements. This meant that operations such as opening connections and executing queries were blocked until completion, which could take a long time. To allow for parallel execution, a developer had to write a multi-threaded application.
Any MySQL client that supports the X Protocol can provide asynchronous execution, either using callbacks, Promises, or by explicitly waiting on a specific result at the moment in time when it is actually needed.
MySQL Shell does not support asynchronous operations.
Using callbacks is a very common way to implement asynchronous operations. When a callback function is specified, the CRUD operation is non-blocking which means that the next statement is called immediately even though the result from the database has not yet been fetched. Only when the result is available is the callback called.
Node.js JavaScript Code
var employees = db.getTable('employee');
employees.select('name', 'age')
.where('name like :name')
.orderBy('name')
.bind('m%')
.execute(function (row) {
// do something with a row
})
}).catch(err) {
// Handle error
});
C# Code
var employees = db.GetTable("employee");
var select = employees.Select("name", "age")
.Where("name like :name")
.OrderBy("name")
.Bind("name", "m%")
.ExecuteAsync();
select.ContinueWith(t =>
{
if (t.Exception != null)
{
// Handle error
}
// Do something with the resultset
});
Java Code
Table employees = db.getTable("employee");
// execute the query asynchronously, obtain a future
CompletableFuture<RowResult> rowsFuture = employees.select("name", "age")
.where("name like :name")
.orderBy("name")
.bind("name", "m%").executeAsync();
// dependent functions can be attached to the CompletableFuture
C++ Code
// Asynchronous execution is not yet implemented in Connector/C++
Languages such as C# can use an async/await pattern.
C# Code
{
Task<RowResult> getEmployeesTask = employees.Select("name", "age")
.Where("name like :name").OrderBy("name")
.Bind("name", "m%").ExecuteAsync();
// Do something else while the getEmployeesTask is executing in the background
// at this point we are ready to get our results back. If it is not done,
// this will block until done
RowResult res = await getEmployeesTask;
foreach (var row in res.FetchAll())
{
// use row object
}
}
Connector/Node.js uses asynchronous operations using Promises for all network operations. See other examples.
Java Code
Table employees = db.getTable("employee");
// execute the query asynchronously, obtain a future
CompletableFuture<RowResult> rowsFuture = employees.select("name", "age")
.where("name like :name")
.orderBy("name")
.bind("name", "m%").executeAsync();
// wait until it's ready
RowResult rows = rowsFuture.get();
C++ Code
// Asynchronous execution is not yet implemented in Connector/C++
Depending on which language you are using, the X DevAPI
may implement a function such as
executeAsync() in exchange for
execute([mysqlx.Async]) or in addition to
execute([mysqlx.Async]).
For example, in a Node.js context all executions are
asynchronous. Therefore, Connector/Node.js does not need to
distinguish between execute() and
executeAsync(). To denote the asynchronous
default execution, Connector/Node.js only implements
execute() which returns JavaScript Promise
objects.
Strongly typed programming languages, such as Java or C#, can
take advantage of having two distinctly named API calls for
synchronous and asynchronous executions. The two calls can have
different return types. For example, Connector/Java can use
execute() to return a
RowResult or DocResult and
executeAsync() to return a
CompletableFuture<T> where the type
parameter is one of the result types.
Consult your language's Connector reference for more details, see Additional Documentation.
Instead of using values directly in an expression string it is
good practice to separate values from the expression string. This
is done using parameters in the expression string and the
bind() function to bind values to the
parameters.
Parameters can be specified in the following ways: anonymous and named.
Parameter Type | Syntax | Example | Allowed in CRUD operations | Allowed in SQL strings |
|---|---|---|---|---|
Anonymous | ? | 'age > ?' | no | yes |
Named | :<name> | 'age > :age' | yes | no |
The following example shows how to use the
bind() function before an
execute() function. For each named parameter,
provide an argument to bind() that contains the
parameter name and its value. The order in which the parameter
value pairs are passed to bind() is of no
importance. The example assumes that the test schema has been
assigned to the variable db and that the
collection my_collection exists.
MySQL Shell and Node.js JavaScript Code
// Collection.find() function with fixed values
var myColl = db.getCollection('my_collection');
var myRes1 = myColl.find('age = 15').execute();
// Using the .bind() function to bind parameters
var myRes2 = myColl.find('name = :param1 AND age = :param2').bind('param1','jack').bind('param2', 17).execute();
// Using named parameters
myColl.modify('name = :param').set('age', 37).
bind('param', 'clare').execute();
// Binding works for all CRUD statements except add()
var myRes3 = myColl.find('name like :param').
bind('param', 'J%').execute();
When running this with Connector/Node.js be aware that
execute() returns a Promise. You might want to
check the results to avoid errors being lost.
MySQL Shell Python Code
# Collection.find() function with hardcoded values
myColl = db.getCollection('my_collection')
myRes1 = myColl.find('age = 15').execute()
# Using the .bind() function to bind parameters
myRes2 = myColl.find('name = :param1 AND age = :param2').bind('param1','jack').bind('param2', 17).execute()
# Using named parameters
myColl.modify('name = :param').set('age', 37).bind('param', 'clare').execute()
# Binding works for all CRUD statements except add()
myRes3 = myColl.find('name like :param').bind('param', 'J%').execute()
C# Code
// Collection.find() function with fixed values
var myColl = db.GetCollection("my_collection");
var myRes1 = myColl.Find("age = 15").Execute();
// Using the .bind() function to bind parameters
var myRes2 = myColl.Find("name = :param1 AND age = :param2").Bind("param1", "jack").Bind("param2", 17).Execute();
// Using named parameters
myColl.Modify("name = :param").Set("age", 37)
.Bind("param", "clare").Execute();
// Binding works for all CRUD statements except add()
var myRes3 = myColl.Find("name like :param")
.Bind("param", "J%").Execute();
Java Code
// Collection.find() function with fixed values
Collection myColl = db.getCollection("my_collection");
DocResult myRes1 = myColl.find("age = 15").execute();
// Using the .bind() function to bind parameters
DocResult myRes2 = myColl.find("name = :param1 AND age = :param2").bind("param1", "jack").bind("param2", 17).execute();
// Using named parameters
myColl.modify("name = :param").set("age", 37)
.bind("param", "clare").execute();
// Using named parameters with a Map
Map<String, Object> params = new HashMap<>();
params.add("name", "clare");
myColl.modify("name = :name").set(".age", 37).bind(params).execute();
// Binding works for all CRUD statements except add()
DocResult myRes3 = myColl.find("name like :param")
.bind("param", "J%").execute();
C++ Code
// Collection.find() function with fixed values
Collection myColl = db.getCollection("my_collection");
auto myRes1 = myColl.find("age = 15").execute();
// Using the .bind() function to bind parameters
auto myRes2 = myColl.find("name = :param1 AND age = :param2"').bind("param1","jack").bind("param2", 17).execute();
// Using named parameters
myColl.modify("name = :param").set("age", 37)
.bind("param", "clare").execute();
// Binding works for all CRUD statements except add()
auto myRes3 = myColl.find("name like :param")
.bind("param", "J%").execute();
Anonymous placeholders are not supported in the X DevAPI.
This restriction improves code clarity in CRUD command chains with
multiple methods using placeholders. Regardless of the
bind() syntax variant used there is always a
clear association between parameters and placeholders based on the
parameter name.
All methods of a CRUD command chain form one namespace for
placeholders. In the following example find()
and fields() are chained. Both methods take an
expression with placeholders. The placeholders refer to one
combined namespace. Both use one placeholder called
:param. A single call to
bind() with one name value parameter for
:param is used to assign a placeholder value to
both occurences of :param in
find() and fields().
MySQL Shell JavaScript Code
// one bind() per parameter
var myColl = db.getCollection('relatives');
var juniors = myColl.find('alias = "jr"').execute().fetchAll();
for (var index in juniors){
myColl.modify('name = :param').
set('parent_name',mysqlx.expr(':param')).
bind('param', juniors[index].name).execute();
}MySQL Shell Python Code
# one bind() per parameter
myColl = db.getCollection('relatives')
juniors = myColl.find('alias = "jr"').execute().fetchAll()
for junior in juniors:
myColl.modify('name = :param'). \
set('parent_name',mysqlx.expr(':param')). \
bind('param', junior.name).execute()
Node.js JavaScript Code
// one bind() per parameter
var myColl = db.getCollection('relatives');
myColl.find('alias = "jr"').execute(function (junior) {
myColl.modify('name = :param').
set('parent_name',mysqlx.expr(':param')).
bind('param', junior.name).execute();
});
C# Code
// one bind() per parameter
myColl.Find("a = :param").Fields(":param as b")
.Bind(new { param = "c"}).Execute();
Java Code
// one bind() per parameter
myColl.find("a = :param").fields(":param as b")
.bind("param", "c"}.execute();
C++ Code
// one bind() per parameter
Collection myColl = db.getCollection("relatives");
DocResult juniors = myColl.find("alias = 'jr'").execute();
DbDoc junior;
while ((junior = juniors.fetchOne()))
{
myColl.modify("name = :param")
.set("parent_name", expr(":param"))
.bind("param", junior["name"]).execute();
}
It is not permitted for a named parameter to use a name that
starts with a digit. For example, :1one and
:1 are not allowed.
Instead of directly binding and executing CRUD operations with
bind() and execute() or
execute() it is also possible to store the
CRUD operation object in a variable for later execution.
The advantage of doing so is to be able to bind several sets of
variables to the parameters defined in the expression strings
and therefore get better performance when executing a large
number of similar operations. The example assumes that the test
schema has been assigned to the variable db
and that the collection my_collection exists.
MySQL Shell JavaScript Code
var myColl = db.getCollection('my_collection');
// Only prepare a Collection.remove() operation, but do not run it yet
var myRemove = myColl.remove('name = :param1 AND age = :param2');
// Binding parameters to the prepared function and .execute()
myRemove.bind('param1', 'mike').bind('param2', 39).execute();
myRemove.bind('param1', 'johannes').bind('param2', 28).execute();
// Binding works for all CRUD statements but add()
var myFind = myColl.find('name like :param1 AND age > :param2');
var myDocs = myFind.bind('param1', 'S%').bind('param2', 18).execute();
var MyOtherDocs = myFind.bind('param1', 'M%').bind('param2', 24).execute();
MySQL Shell Python Code
myColl = db.getCollection('my_collection')
# Only prepare a Collection.remove() operation, but do not run it yet
myRemove = myColl.remove('name = :param1 AND age = :param2')
# Binding parameters to the prepared function and .execute()
myRemove.bind('param1', 'mike').bind('param2', 39).execute()
myRemove.bind('param1', 'johannes').bind('param2', 28).execute()
# Binding works for all CRUD statements but add()
myFind = myColl.find('name like :param1 AND age > :param2')
myDocs = myFind.bind('param1', 'S%').bind('param2', 18).execute()
MyOtherDocs = myFind.bind('param1', 'M%').bind('param2', 24).execute()
Node.js JavaScript Code
var myColl = db.getCollection('my_collection');
// Only prepare a Collection.remove() operation, but do not run it yet
var myRemove = myColl.remove('name = :param1 AND age = :param2');
// Binding parameters to the prepared function and .execute()
myRemove.bind('param1', 'mike').bind('param2', 39).execute();
myRemove.bind('param1', 'johannes').bind('param2', 28).execute();
// Binding works for all CRUD statements but add()
var myFind = myColl.find('name like :param1 AND age > :param2');
var myDocs = myFind.bind('param1', 'S%').bind('param2', 18).execute();
var MyOtherDocs = myFind.bind('param1', 'M%').bind('param2', 24).execute();
C# Code
var myColl = db.GetCollection("my_collection");
// Only prepare a Collection.Remove() operation, but do not run it yet
var myRemove = myColl.Remove("name = :param1 AND age = :param2");
// Binding parameters to the prepared function and .Execute()
myRemove.Bind("param1", "mike").Bind("param2", 39).Execute();
myRemove.Bind("param1", "johannes").Bind("param2", 28).Execute();
// Binding works for all CRUD statements but Add()
var myFind = myColl.Find("name like :param1 AND age > :param2");
var myDocs = myFind.Bind("param1", "S%").Bind("param2", 18).Execute();
var MyOtherDocs = myFind.Bind("param1", "M%").Bind("param2", 24).Execute();
Java Code
Collection myColl = db.getCollection("my_collection");
// Create Collection.remove() operation, but do not run it yet
RemoveStatement myRemove = myColl.remove("name = :param1 AND age = :param2");
// Binding parameters to the prepared function and .execute()
myRemove.bind("param1", "mike").bind("param2", 39).execute();
myRemove.bind("param1", "johannes").bind("param2", 28).execute();
// Binding works for all CRUD statements but add()
FindStatement myFind = myColl.find("name LIKE :name AND age > :age");
Map<String, Object> params = new HashMap<>();
params.put("name", "S%");
params.put("age", 18);
DocResult myDocs = myFind.bind(params).execute();
params.put("name", "M%");
params.put("age", 24);
DocResult myOtherDocs = myFind.bind(params).execute();
C++ Code
// Ths functionality is not yet implemented in Connector/C++. At the // moment a crud operation can be executed only once, with one set // of parameter values.
Using X DevAPI in a programming language that fully
specifies the syntax to be used, for example, when executing SQL
statements though a NodeSession or working with any of the CRUD
operations, the actual operation is performed only when the
execute() function is called. For example:
var result = session.sql('show databases').execute();
var city_res = db.cities.find().execute();
The call of the execute() function above causes
the operation to be executed and return a Result object. The
returned Result object is then assigned to a variable, and the
assignment is the last operation executed, which returns no data.
Such operations can also return a Result object, which is used to
process the information returned from the operation.
Alternatively MySQL Shell provides the following usability features that make it easier to work with the X DevAPI interactively:
Automatic execution of CRUD and SQL operations.
Automatic processing of results.
To achieve this functionality MySQL Shell monitors the result of the last operation executed every time you enter a statement. The combination of these features makes using the MySQL Shell interactive mode ideal for prototyping code, as operations are executed immediately and their results are displayed without requiring any additional coding. For more information see MySQL Shell User Guide.
If MySQL Shell detects that a CRUD operation ready to execute
has been returned, it automatically calls the
execute() function. Repeating the examples
above in MySQL Shell and removing the assignment operation
shows they are automatically executed.
mysql-js> session.sql('show databases');MySQL Shell executes the SQL operation, and as mentioned above, once this operation is executed a Result object is returned.
If MySQL Shell detects that a Result object is going to be returned, it automatically processes it, printing the result data in the best format possible. There are different types of Result objects and the format changes across them.
mysql-js> db.countryInfo.find().limit(1)
[
{
"GNP": 828,
"IndepYear": null,
"Name": "Aruba",
"_id": "ABW",
"demographics": {
"LifeExpectancy": 78.4000015258789,
"Population": 103000
},
"geography": {
"Continent": "North America",
"Region": "Caribbean",
"SurfaceArea": 193
},
"government": {
"GovernmentForm": "Nonmetropolitan Territory of The Netherlands",
"HeadOfState": "Beatrix"
}
}
]
1 document in set (0.00 sec)