Beta Draft: 2016-08-16

Chapter 3 CRUD Operations

Table of Contents

3.1 CRUD Operations Overview
3.2 Method Chaining
3.3 Synchronous versus Asynchronous Execution
3.4 Parameter Binding
3.5 MySQL Shell Automatic Code Execution

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.

3.1 CRUD Operations Overview

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.

Database Object Classes

Figure 3.1 Database Object - Class Diagram

Database Object - Class Diagram

3.2 Method Chaining

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();

3.3 Synchronous versus Asynchronous Execution

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.

Note

MySQL Shell does not support asynchronous operations.

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++

Asynchronous Operations using Awaits

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++

Syntax Differences

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.

3.4 Parameter Binding

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.

Preparing CRUD Statements

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.

3.5 MySQL Shell Automatic Code Execution

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.

Automatic Code Execution

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.

Automatic Result Processing

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)