docs: update README with lb4 style
This commit is contained in:
parent
eaf5d6858e
commit
d40adc4211
750
README.md
750
README.md
|
@ -1,11 +1,8 @@
|
||||||
# loopback-connector-mysql
|
# loopback-connector-mysql
|
||||||
|
|
||||||
[MySQL](https://www.mysql.com/) is a popular open-source relational database management system (RDBMS). The `loopback-connector-mysql` module provides the MySQL connector module for the LoopBack framework.
|
[MySQL](https://www.mysql.com/) is a popular open-source relational database
|
||||||
|
management system (RDBMS). The `loopback-connector-mysql` module provides the
|
||||||
<div class="gh-only">See also <a href="http://loopback.io/doc/en/lb3/MySQL-connector.html">LoopBack MySQL Connector</a> in LoopBack documentation.
|
MySQL connector module for the LoopBack framework.
|
||||||
<br/><br/>
|
|
||||||
<b>NOTE</b>: The MySQL connector requires MySQL 5.0+.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -15,15 +12,48 @@ In your application root directory, enter this command to install the connector:
|
||||||
npm install loopback-connector-mysql --save
|
npm install loopback-connector-mysql --save
|
||||||
```
|
```
|
||||||
|
|
||||||
This installs the module from npm and adds it as a dependency to the application's `package.json` file.
|
**Note**: The MySQL connector requires MySQL 5.0+.
|
||||||
|
|
||||||
If you create a MySQL data source using the data source generator as described below, you don't have to do this, since the generator will run `npm install` for you.
|
This installs the module from npm and adds it as a dependency to the
|
||||||
|
application's `package.json` file.
|
||||||
|
|
||||||
|
If you create a MySQL data source using the data source generator as described
|
||||||
|
below, you don't have to do this, since the generator will run `npm install` for
|
||||||
|
you.
|
||||||
|
|
||||||
## Creating a MySQL data source
|
## Creating a MySQL data source
|
||||||
|
|
||||||
Use the [Data source generator](http://loopback.io/doc/en/lb3/Data-source-generator.html) to add a MySQL data source to your application.
|
For LoopBack 4 users, use the LoopBack 4
|
||||||
The generator will prompt for the database server hostname, port, and other settings
|
[Command-line interface](https://loopback.io/doc/en/lb4/Command-line-interface.html)
|
||||||
required to connect to a MySQL database. It will also run the `npm install` command above for you.
|
to generate a DataSource with MySQL connector to your LB4 application. Run
|
||||||
|
[`lb4 datasource`](https://loopback.io/doc/en/lb4/DataSource-generator.html), it
|
||||||
|
will prompt for configurations such as host, post, etc. that are required to
|
||||||
|
connect to a MySQL database.
|
||||||
|
|
||||||
|
After setting it up, the configuration can be found under
|
||||||
|
`src/datasources/<DataSourceName>.datasource.ts`, which would look like this:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const config = {
|
||||||
|
name: 'db',
|
||||||
|
connector: 'mysql',
|
||||||
|
url: '',
|
||||||
|
host: 'localhost',
|
||||||
|
port: 3306,
|
||||||
|
user: 'user',
|
||||||
|
password: 'pass',
|
||||||
|
database: 'testdb',
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>For LoopBack 3 users</strong></summary>
|
||||||
|
|
||||||
|
Use
|
||||||
|
the [Data source generator](http://loopback.io/doc/en/lb3/Data-source-generator.html) to
|
||||||
|
add a MySQL data source to your application.
|
||||||
|
The generator will prompt for the database server hostname, port, and other
|
||||||
|
settings required to connect to a MySQL database. It will also run the
|
||||||
|
`npm install` command above for you.
|
||||||
|
|
||||||
The entry in the application's `/server/datasources.json` will look like this:
|
The entry in the application's `/server/datasources.json` will look like this:
|
||||||
|
|
||||||
|
@ -39,7 +69,10 @@ The entry in the application's `/server/datasources.json` will look like this:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit `datasources.json` to add any other additional properties that you require.
|
</details>
|
||||||
|
|
||||||
|
Edit `<DataSourceName>.datasources.ts` to add any other additional properties
|
||||||
|
that you require.
|
||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
|
|
||||||
|
@ -120,11 +153,13 @@ Edit `datasources.json` to add any other additional properties that you require.
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
**NOTE**: In addition to these properties, you can use additional parameters supported by [`node-mysql`](https://github.com/felixge/node-mysql).
|
**NOTE**: In addition to these properties, you can use additional parameters
|
||||||
|
supported by [`node-mysql`](https://github.com/felixge/node-mysql).
|
||||||
|
|
||||||
## Type mappings
|
## Type mappings
|
||||||
|
|
||||||
See [LoopBack types](http://loopback.io/doc/en/lb3/LoopBack-types.html) for details on LoopBack's data types.
|
See [LoopBack types](http://loopback.io/doc/en/lb3/LoopBack-types.html) for
|
||||||
|
details on LoopBack's data types.
|
||||||
|
|
||||||
### LoopBack to MySQL types
|
### LoopBack to MySQL types
|
||||||
|
|
||||||
|
@ -209,127 +244,149 @@ See [LoopBack types](http://loopback.io/doc/en/lb3/LoopBack-types.html) for de
|
||||||
|
|
||||||
_NOTE_ as of v3.0.0 of MySQL Connector, the following flags were introduced:
|
_NOTE_ as of v3.0.0 of MySQL Connector, the following flags were introduced:
|
||||||
|
|
||||||
- `treatCHAR1AsString`
|
- `treatCHAR1AsString` default `false` - treats CHAR(1) as a String instead of a
|
||||||
default `false` - treats CHAR(1) as a String instead of a Boolean
|
Boolean
|
||||||
- `treatBIT1AsBit`
|
- `treatBIT1AsBit` default `true` - treats BIT(1) as a Boolean instead of a
|
||||||
default `true` - treats BIT(1) as a Boolean instead of a Binary
|
Binary
|
||||||
- `treatTINYINT1AsTinyInt`
|
- `treatTINYINT1AsTinyInt` default `true` - treats TINYINT(1) as a Boolean
|
||||||
default `true` - treats TINYINT(1) as a Boolean instead of a Number
|
instead of a Number
|
||||||
|
|
||||||
## Using the datatype field/column option with MySQL
|
## Data mapping properties
|
||||||
|
|
||||||
Use the `mysql` model property to specify additional MySQL-specific properties for a LoopBack model.
|
### Table/Column Names
|
||||||
|
|
||||||
For example:
|
Besides the basic LoopBack types, as we introduced above, you can also specify
|
||||||
|
additional MySQL-specific properties for a LoopBack model. It would be mapped to
|
||||||
|
the database.
|
||||||
|
|
||||||
{% include code-caption.html content="/common/models/model.json" %}
|
Use the `mysql.<property>` in the model definition or the property definition to
|
||||||
|
configure the table/column definition.
|
||||||
|
|
||||||
|
For example, the following settings would allow you to have custom table name
|
||||||
|
(`Custom_User`) and column name (`custom_id` and `custom_name`). Such mapping is
|
||||||
|
useful when you'd like to have different table/column names from the model:
|
||||||
|
|
||||||
|
{% include code-caption.html content="user.model.ts" %}
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@model({
|
||||||
|
settings: { mysql: { schema: 'testdb', table: 'Custom_User'} },
|
||||||
|
})
|
||||||
|
export class User extends Entity {
|
||||||
|
@property({
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
id: true,
|
||||||
|
mysql: {
|
||||||
|
columnName: 'custom_id',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'string',
|
||||||
|
mysql: {
|
||||||
|
columnName: 'custom_name',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
name?: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>For LoopBack 3 users</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
"locationId":{
|
{
|
||||||
"type":"String",
|
"name": "User",
|
||||||
"required":true,
|
"options": {
|
||||||
"length":20,
|
"mysql": {
|
||||||
"mysql":
|
"schema": "testdb",
|
||||||
{
|
"table": "Custom_User"
|
||||||
"columnName":"LOCATION_ID",
|
|
||||||
"dataType":"VARCHAR",
|
|
||||||
"dataLength":20,
|
|
||||||
"nullable":"N"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "Number",
|
||||||
|
"required": true,
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "custom_id",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "String",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "custom_name",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use the dataType column/property attribute to specify what MySQL column type to use for many loopback-datasource-juggler types.
|
</details>
|
||||||
The following type-dataType combinations are supported:
|
|
||||||
|
|
||||||
- Number
|
### Numeric Types
|
||||||
|
|
||||||
|
Except the names, you can also use the dataType column/property attribute to
|
||||||
|
specify what MySQL column type to use. The following MySQL type-dataType
|
||||||
|
combinations are supported:
|
||||||
|
|
||||||
|
- number
|
||||||
- integer
|
- integer
|
||||||
- tinyint
|
- tinyint
|
||||||
- smallint
|
- smallint
|
||||||
- mediumint
|
- mediumint
|
||||||
- int
|
- int
|
||||||
- bigint
|
- bigint
|
||||||
|
- float
|
||||||
|
- double
|
||||||
|
- decimal
|
||||||
|
|
||||||
Use the `limit` option to alter the display width. Example:
|
The following examples will be in LoopBack 4 style, but it's the same if you
|
||||||
|
provide `mysql.<property>` to the LB3 property definition.
|
||||||
|
|
||||||
```javascript
|
#### Floating-point types
|
||||||
{ userName : {
|
|
||||||
type: String,
|
|
||||||
dataType: 'char',
|
|
||||||
limit: 24
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Default Clause/Constant
|
For Float and Double data types, use the `precision` and `scale` options to
|
||||||
|
specify custom precision. Default is (16,8).
|
||||||
|
|
||||||
Use the `default` property to have MySQL handle setting column `DEFAULT` value.
|
<details><summary markdown="span"><strong>Example</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```ts
|
||||||
"status": {
|
@property({
|
||||||
"type": "string",
|
type: 'Number',
|
||||||
"mysql": {
|
mysql: {
|
||||||
"default": "pending"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"number": {
|
|
||||||
"type": "number",
|
|
||||||
"mysql": {
|
|
||||||
"default": 256
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For the date or timestamp types use `CURRENT_TIMESTAMP` or `now`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
"last_modified": {
|
|
||||||
"type": "date",
|
|
||||||
"mysql": {
|
|
||||||
"default":"CURRENT_TIMESTAMP"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**NOTE**: The following column types do **NOT** supported [MySQL Default Values](https://dev.mysql.com/doc/refman/5.7/en/data-type-defaults.html):
|
|
||||||
|
|
||||||
- BLOB
|
|
||||||
- TEXT
|
|
||||||
- GEOMETRY
|
|
||||||
- JSON
|
|
||||||
|
|
||||||
### Floating-point types
|
|
||||||
|
|
||||||
For Float and Double data types, use the `precision` and `scale` options to specify custom precision. Default is (16,8). For example:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{ average :
|
|
||||||
{ type: Number,
|
|
||||||
dataType: 'float',
|
dataType: 'float',
|
||||||
precision: 20,
|
precision: 20,
|
||||||
scale: 4
|
scale: 4
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
price: Number;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fixed-point exact value types
|
</details>
|
||||||
|
|
||||||
For Decimal and Numeric types, use the `precision` and `scale` options to specify custom precision. Default is (9,2).
|
#### Fixed-point exact value types
|
||||||
These aren't likely to function as true fixed-point.
|
|
||||||
|
|
||||||
Example:
|
For Decimal and Numeric types, use the `precision` and `scale` options to
|
||||||
|
specify custom precision. Default is (9,2). These aren't likely to function as
|
||||||
|
true fixed-point.
|
||||||
|
|
||||||
```javascript
|
<details><summary markdown="span"><strong>Example</strong></summary>
|
||||||
{ stdDev :
|
|
||||||
{ type: Number,
|
```ts
|
||||||
|
@property({
|
||||||
|
type: 'Number',
|
||||||
|
mysql: {
|
||||||
dataType: 'decimal',
|
dataType: 'decimal',
|
||||||
precision: 12,
|
precision: 12,
|
||||||
scale: 8
|
scale: 8
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
price: Number;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Other types
|
</details>
|
||||||
|
|
||||||
|
### Text types
|
||||||
|
|
||||||
Convert String / DataSource.Text / DataSource.JSON to the following MySQL types:
|
Convert String / DataSource.Text / DataSource.JSON to the following MySQL types:
|
||||||
|
|
||||||
|
@ -340,190 +397,447 @@ Convert String / DataSource.Text / DataSource.JSON to the following MySQL types:
|
||||||
- tinytext
|
- tinytext
|
||||||
- longtext
|
- longtext
|
||||||
|
|
||||||
Example:
|
<details><summary markdown="span"><strong>Example</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```ts
|
||||||
{ userName :
|
@property({
|
||||||
{ type: String,
|
type: 'String',
|
||||||
|
mysql: {
|
||||||
dataType: 'char',
|
dataType: 'char',
|
||||||
limit: 24
|
dataLength: 24 // limits the property length
|
||||||
}
|
},
|
||||||
}
|
})
|
||||||
|
userName: String;
|
||||||
```
|
```
|
||||||
|
|
||||||
Example:
|
</details>
|
||||||
|
|
||||||
```javascript
|
### Dat types
|
||||||
{ biography :
|
|
||||||
{ type: String,
|
Convert JSON Date types to datetime or timestamp.
|
||||||
dataType: 'longtext'
|
|
||||||
}
|
<details><summary markdown="span"><strong>Example</strong></summary>
|
||||||
}
|
|
||||||
|
```ts
|
||||||
|
@property({
|
||||||
|
type: 'Date',
|
||||||
|
mysql: {
|
||||||
|
dataType: 'timestamp',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
startTime: Date;
|
||||||
```
|
```
|
||||||
|
|
||||||
Convert JSON Date types to datetime or timestamp
|
</details>
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{ startTime :
|
|
||||||
{ type: Date,
|
|
||||||
dataType: 'timestamp'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enum
|
### Enum
|
||||||
|
|
||||||
Enums are special. Create an Enum using Enum factory:
|
Enums are special. Create an Enum using Enum factory:
|
||||||
|
|
||||||
```javascript
|
```ts
|
||||||
var MOOD = dataSource.EnumFactory('glad', 'sad', 'mad');
|
const MOOD = dataSource.EnumFactory('glad', 'sad', 'mad');
|
||||||
MOOD.SAD; // 'sad'
|
MOOD.SAD; // 'sad'
|
||||||
MOOD(2); // 'sad'
|
MOOD(2); // 'sad'
|
||||||
MOOD('SAD'); // 'sad'
|
MOOD('SAD'); // 'sad'
|
||||||
MOOD('sad'); // 'sad'
|
MOOD('sad'); // 'sad'
|
||||||
{ mood: { type: MOOD }}
|
|
||||||
{ choice: { type: dataSource.EnumFactory('yes', 'no', 'maybe'), null: false }}
|
export class User extends Entity {
|
||||||
|
//..
|
||||||
|
@property({
|
||||||
|
type: MOOD,
|
||||||
|
})
|
||||||
|
mood: MOOD;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Default Clause/Constant
|
||||||
|
|
||||||
|
Use the `default` property to have MySQL handle setting column `DEFAULT` value.
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>Example</strong></summary>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@property({
|
||||||
|
type: 'String',
|
||||||
|
mysql: {
|
||||||
|
default: 'pending'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
status: String;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'Number',
|
||||||
|
mysql: {
|
||||||
|
default: 42
|
||||||
|
}
|
||||||
|
})
|
||||||
|
maxDays: Number;
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
For the date or timestamp types use `CURRENT_TIMESTAMP` or `now`.
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>Example</strong></summary>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@property({
|
||||||
|
type: 'Date',
|
||||||
|
mysql: {
|
||||||
|
default: 'CURRENT_TIMESTAMP'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
last_modified: Date;
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**NOTE**: The following column types do **NOT** supported
|
||||||
|
[MySQL Default Values](https://dev.mysql.com/doc/refman/5.7/en/data-type-defaults.html):
|
||||||
|
|
||||||
|
- BLOB
|
||||||
|
- TEXT
|
||||||
|
- GEOMETRY
|
||||||
|
- JSON
|
||||||
|
|
||||||
## Discovery and auto-migration
|
## Discovery and auto-migration
|
||||||
|
|
||||||
### Model discovery
|
### Model discovery
|
||||||
|
|
||||||
The MySQL connector supports _model discovery_ that enables you to create LoopBack models
|
The MySQL connector supports _model discovery_ that enables you to create
|
||||||
based on an existing database schema using the unified [database discovery API](http://apidocs.strongloop.com/loopback-datasource-juggler/#datasource-prototype-discoverandbuildmodels). For more information on discovery, see [Discovering models from relational databases](https://loopback.io/doc/en/lb3/Discovering-models-from-relational-databases.html).
|
LoopBack models based on an existing database schema. Once you defined your
|
||||||
|
datasource:
|
||||||
|
|
||||||
|
- LoopBack 4 users could use the commend
|
||||||
|
[`lb4 discover`](https://loopback.io/doc/en/lb4/Discovering-models.html) to
|
||||||
|
discover models.
|
||||||
|
- For LB3 users, please check
|
||||||
|
[Discovering models from relational databases](https://loopback.io/doc/en/lb3/Discovering-models-from-relational-databases.html).
|
||||||
|
(See
|
||||||
|
[database discovery API](http://apidocs.strongloop.com/loopback-datasource-juggler/#datasource-prototype-discoverandbuildmodels)
|
||||||
|
for related APIs information)
|
||||||
|
|
||||||
### Auto-migration
|
### Auto-migration
|
||||||
|
|
||||||
The MySQL connector also supports _auto-migration_ that enables you to create a database schema
|
The MySQL connector also supports _auto-migration_ that enables you to create a
|
||||||
from LoopBack models using the [LoopBack automigrate method](http://apidocs.strongloop.com/loopback-datasource-juggler/#datasource-prototype-automigrate).
|
database schema from LoopBack models. For example, based on the following model,
|
||||||
|
the auto-migration method would create/alter existing `Customer` table in the
|
||||||
|
database. Table `Customer` would have two columns: `name` and `id`, where `id`
|
||||||
|
is also the primary key that has `auto_increment` set as it has definition of
|
||||||
|
`type: 'Number'` and `generated: true`:
|
||||||
|
|
||||||
For more information on auto-migration, see [Creating a database schema from models](https://loopback.io/doc/en/lb3/Creating-a-database-schema-from-models.html) for more information.
|
```ts
|
||||||
|
@model()
|
||||||
|
export class Customer extends Entity {
|
||||||
|
@property({
|
||||||
|
id: true,
|
||||||
|
type: 'Number',
|
||||||
|
generated: true,
|
||||||
|
})
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Moreover, additional MySQL-specific properties mentioned in the
|
||||||
|
[Data mapping properties](#data-mapping-properties) section work with
|
||||||
|
auto-migration as well.
|
||||||
|
|
||||||
|
#### Auto-generated ids
|
||||||
|
|
||||||
|
For now LoopBack MySQL connector only supports auto-generated id
|
||||||
|
(`generated: true`) for integer type as for MySQL, the default id type is
|
||||||
|
_integer_. If you'd like to use other types such as string (uuid) as the id
|
||||||
|
type, you can:
|
||||||
|
|
||||||
|
- use uuid that is **generated by your LB application** by setting
|
||||||
|
[`defaultFn: uuid`](https://loopback.io/doc/en/lb4/Model.html#property-decorator).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@property({
|
||||||
|
id: true,
|
||||||
|
type: 'string'
|
||||||
|
defaultFn: 'uuidv4',
|
||||||
|
// generated: true, -> not needed
|
||||||
|
})
|
||||||
|
id: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
- Alter the table in your database to use a certain function if you prefer
|
||||||
|
having **the database to generate the value**.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@property({
|
||||||
|
id: true,
|
||||||
|
type: 'string'
|
||||||
|
generated: true, // to indicate the value generates by the db
|
||||||
|
useDefaultIdType: false, // needed
|
||||||
|
})
|
||||||
|
id: string;
|
||||||
|
```
|
||||||
|
|
||||||
#### Auto-migrate/Auto-update models with foreign keys
|
#### Auto-migrate/Auto-update models with foreign keys
|
||||||
|
|
||||||
MySQL handles the foreign key integrity of the related models upon auto-migrate or auto-update operation. It first deletes any related models before calling delete on the models with the relationship.
|
Foreign key constraints can be defined in the model definition.
|
||||||
|
|
||||||
Example:
|
**Note**: The order of table creation is important. A referenced table must
|
||||||
|
exist before creating a foreign key constraint.
|
||||||
|
|
||||||
|
Define your models and the foreign key constraints as follows:
|
||||||
|
|
||||||
|
{% include code-caption.html content="customer.model.ts" %}
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@model()
|
||||||
|
export class Customer extends Entity {
|
||||||
|
@property({
|
||||||
|
id: true,
|
||||||
|
type: 'Number',
|
||||||
|
generated: true,
|
||||||
|
})
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`order.model.ts`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@model({
|
||||||
|
settings: {
|
||||||
|
foreignKeys: {
|
||||||
|
fk_order_customerId: {
|
||||||
|
name: 'fk_order_customerId',
|
||||||
|
entity: 'Customer',
|
||||||
|
entityKey: 'id',
|
||||||
|
foreignKey: 'customerId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class Order extends Entity {
|
||||||
|
@property({
|
||||||
|
id: true,
|
||||||
|
type: 'Number',
|
||||||
|
generated: true
|
||||||
|
})
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'string'
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'Number'
|
||||||
|
})
|
||||||
|
customerId: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>For LoopBack 3 users</strong></summary>
|
||||||
|
|
||||||
|
```json
|
||||||
|
({
|
||||||
|
"name": "Customer",
|
||||||
|
"options": {
|
||||||
|
"idInjection": false
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "Number",
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "String",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Order",
|
||||||
|
"options": {
|
||||||
|
"idInjection": false,
|
||||||
|
"foreignKeys": {
|
||||||
|
"fk_order_customerId": {
|
||||||
|
"name": "fk_order_customerId",
|
||||||
|
"entity": "Customer",
|
||||||
|
"entityKey": "id",
|
||||||
|
"foreignKey": "customerId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "Number"
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
"customerId": {
|
||||||
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "String",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
MySQL handles the foreign key integrity by the referential action specified by
|
||||||
|
`ON UPDATE` and `ON DELETE`. You can specify which referential actions the
|
||||||
|
foreign key follows in the model definition upon auto-migrate or auto-update
|
||||||
|
operation. Both `onDelete` and `onUpdate` default to `restrict`.
|
||||||
|
|
||||||
|
Take the example we showed above, let's add the referential action to the
|
||||||
|
foreign key `customerId`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@model({
|
||||||
|
settings: {
|
||||||
|
foreignKeys: {
|
||||||
|
fk_order_customerId: {
|
||||||
|
name: 'fk_order_customerId',
|
||||||
|
entity: 'Customer',
|
||||||
|
entityKey: 'id',
|
||||||
|
foreignKey: 'customerId',
|
||||||
|
onUpdate: 'restrict', // restrict|cascade|set null|no action|set default
|
||||||
|
onDelete: 'cascade' // restrict|cascade|set null|no action|set default
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class Order extends Entity {
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>For LoopBack 3 users</strong></summary>
|
||||||
|
|
||||||
**model-definiton.json**
|
**model-definiton.json**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "Book",
|
"name": "Customer",
|
||||||
"base": "PersistedModel",
|
"options": {
|
||||||
"idInjection": false,
|
"idInjection": false
|
||||||
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"bId": {
|
"id": {
|
||||||
"type": "number",
|
"type": "Number",
|
||||||
"id": true,
|
"id": 1
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "String",
|
||||||
},
|
"required": false
|
||||||
"isbn": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"validations": [],
|
|
||||||
"relations": {
|
|
||||||
"author": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "Author",
|
|
||||||
"foreignKey": "authorId"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"acls": [],
|
|
||||||
"methods": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"authorId": {
|
|
||||||
"name": "authorId",
|
|
||||||
"foreignKey": "authorId",
|
|
||||||
"entityKey": "aId",
|
|
||||||
"entity": "Author",
|
|
||||||
"onUpdate": "restrict",
|
|
||||||
"onDelete": "restrict"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
{
|
||||||
"name": "Author",
|
"name": "Order",
|
||||||
"base": "PersistedModel",
|
"options": {
|
||||||
"idInjection": false,
|
"idInjection": false,
|
||||||
"properties": {
|
"foreignKeys": {
|
||||||
"aId": {
|
"fk_order_customerId": {
|
||||||
"type": "number",
|
"name": "fk_order_customerId",
|
||||||
"id": true,
|
"entity": "Customer",
|
||||||
"required": true
|
"entityKey": "id",
|
||||||
},
|
"foreignKey": "customerId",
|
||||||
"name": {
|
"onUpdate": "restrict",
|
||||||
"type": "string"
|
"onDelete": "cascade"
|
||||||
},
|
}
|
||||||
"dob": {
|
|
||||||
"type": "date"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validations": [],
|
"properties": {
|
||||||
"relations": {},
|
"id": {
|
||||||
"acls": [],
|
"type": "Number"
|
||||||
"methods": {}
|
"id": 1
|
||||||
|
},
|
||||||
|
"customerId": {
|
||||||
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "String",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**boot-script.js**
|
**boot-script.js**
|
||||||
|
|
||||||
```js
|
```js
|
||||||
module.exports = function(app) {
|
module.exports = function (app) {
|
||||||
var mysqlDs = app.dataSources.mysqlDS;
|
var mysqlDs = app.dataSources.mysqlDS;
|
||||||
var Book = app.models.Book;
|
var Book = app.models.Order;
|
||||||
var Author = app.models.Author;
|
var Author = app.models.Customer;
|
||||||
|
|
||||||
// first autoupdate the `Author` model to avoid foreign key constraint failure
|
// first autoupdate the `Customer` model to avoid foreign key constraint failure
|
||||||
mysqlDs.autoupdate('Author', function(err) {
|
mysqlDs.autoupdate('Customer', function (err) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
console.log('\nAutoupdated table `Author`.');
|
console.log('\nAutoupdated table `Customer`.');
|
||||||
|
|
||||||
mysqlDs.autoupdate('Book', function(err) {
|
mysqlDs.autoupdate('Order', function (err) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
console.log('\nAutoupdated table `Book`.');
|
console.log('\nAutoupdated table `Order`.');
|
||||||
// at this point the database table `Book` should have one foreign key `authorId` integrated
|
// at this point the database table `Order` should have one foreign key `customerId` integrated
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
#### Breaking Changes with GeoPoint since 5.x
|
#### Breaking Changes with GeoPoint since 5.x
|
||||||
|
|
||||||
Prior to `loopback-connector-mysql@5.x`, MySQL connector was saving and loading GeoPoint properties from the MySQL database in reverse.
|
Prior to `loopback-connector-mysql@5.x`, MySQL connector was saving and loading
|
||||||
MySQL expects values to be POINT(X, Y) or POINT(lng, lat), but the connector was saving them in the opposite order(i.e. POINT(lat,lng)).
|
GeoPoint properties from the MySQL database in reverse. MySQL expects values to
|
||||||
If you have an application with a model that has a GeoPoint property using previous versions of this connector, you can migrate your models
|
be `POINT(X, Y)` or `POINT(lng, lat)`, but the connector was saving them in the
|
||||||
using the following programmatic approach:
|
opposite order(i.e. `POINT(lat,lng)`).
|
||||||
**NOTE** Please back up the database tables that have your application data before performing any of the steps.
|
|
||||||
|
Use the `geopoint` type to achieve so:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@property({
|
||||||
|
type: 'geopoint'
|
||||||
|
})
|
||||||
|
name: GeoPoint;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have an application with a model that has a GeoPoint property using
|
||||||
|
previous versions of this connector, you can migrate your models using the
|
||||||
|
following programmatic approach:
|
||||||
|
|
||||||
|
<details><summary markdown="span"><strong>Click here to expend</strong></summary>
|
||||||
|
|
||||||
|
**NOTE** Please back up the database tables that have your application data
|
||||||
|
before performing any of the steps.
|
||||||
|
|
||||||
1. Create a boot script under `server/boot/` directory with the following:
|
1. Create a boot script under `server/boot/` directory with the following:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
'use strict';
|
'use strict';
|
||||||
module.exports = function(app) {
|
module.exports = function (app) {
|
||||||
function findAndUpdate() {
|
function findAndUpdate() {
|
||||||
var teashop = app.models.teashop;
|
var teashop = app.models.teashop;
|
||||||
//find all instances of the model we'd like to migrate
|
//find all instances of the model we'd like to migrate
|
||||||
teashop.find({}, function(err, teashops) {
|
teashop.find({}, function (err, teashops) {
|
||||||
teashops.forEach(function(teashopInstance) {
|
teashops.forEach(function (teashopInstance) {
|
||||||
//what we fetch back from the db is wrong, so need to revert it here
|
//what we fetch back from the db is wrong, so need to revert it here
|
||||||
var newLocation = {
|
var newLocation = {
|
||||||
lng: teashopInstance.location.lat,
|
lng: teashopInstance.location.lat,
|
||||||
lat: teashopInstance.location.lng
|
lat: teashopInstance.location.lng,
|
||||||
};
|
};
|
||||||
//only update the GeoPoint property for the model
|
//only update the GeoPoint property for the model
|
||||||
teashopInstance.updateAttribute('location', newLocation, function(
|
teashopInstance.updateAttribute('location', newLocation, function (
|
||||||
err,
|
err,
|
||||||
inst
|
inst,
|
||||||
) {
|
) {
|
||||||
if (err) console.log('update attribute failed', err);
|
if (err) console.log('update attribute failed', err);
|
||||||
else console.log('updateAttribute successful');
|
else console.log('updateAttribute successful');
|
||||||
|
@ -564,11 +878,14 @@ For the above example, the model definition is as follows:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## Running tests
|
## Running tests
|
||||||
|
|
||||||
### Own instance
|
### Own instance
|
||||||
|
|
||||||
If you have a local or remote MySQL instance and would like to use that to run the test suite, use the following command:
|
If you have a local or remote MySQL instance and would like to use that to run
|
||||||
|
the test suite, use the following command:
|
||||||
|
|
||||||
- Linux
|
- Linux
|
||||||
|
|
||||||
|
@ -584,15 +901,20 @@ SET MYSQL_HOST=<HOST> SET MYSQL_PORT=<PORT> SET MYSQL_USER=<USER> SET MYSQL_PASS
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
If you do not have a local MySQL instance, you can also run the test suite with very minimal requirements.
|
If you do not have a local MySQL instance, you can also run the test suite with
|
||||||
|
very minimal requirements.
|
||||||
|
|
||||||
- Assuming you have [Docker](https://docs.docker.com/engine/installation/) installed, run the following script which would spawn a MySQL instance on your local:
|
- Assuming you have [Docker](https://docs.docker.com/engine/installation/)
|
||||||
|
installed, run the following script which would spawn a MySQL instance on your
|
||||||
|
local:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
source setup.sh <HOST> <PORT> <USER> <PASSWORD> <DATABASE>
|
source setup.sh <HOST> <PORT> <USER> <PASSWORD> <DATABASE>
|
||||||
```
|
```
|
||||||
|
|
||||||
where `<HOST>`, `<PORT>`, `<USER>`, `<PASSWORD>` and `<DATABASE>` are optional parameters. The default values are `localhost`, `3306`, `root`, `pass` and `testdb` respectively.
|
where `<HOST>`, `<PORT>`, `<USER>`, `<PASSWORD>` and `<DATABASE>` are optional
|
||||||
|
parameters. The default values are `localhost`, `3306`, `root`, `pass` and
|
||||||
|
`testdb` respectively.
|
||||||
|
|
||||||
- Run the test:
|
- Run the test:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue