One-to-One Relationship Tutorial
The core feature of an Object Relationship Mapping (ORM) database is to create relationships. The one-to-one relationship is used to constrain the cardinality to a single object. This tutorial demonstrates how to define and use a one-to-one relationship within Onyx Database.
Steps to Define a One-to-One Relationship (Onyx Cloud Database)
1
Declare the Entities
Use the provided code snippet to declare your entities with one-to-one relationships and deploy the database using this schema definition.
1{
2 "tables": [
3 {
4 "name": "Sailboat",
5 "identifier": {
6 "name": "registrationCode",
7 "type": "String",
8 "generator": "None"
9 },
10 "partition": "",
11 "attributes": [
12 {
13 "name": "registrationCode",
14 "type": "String"
15 },
16 {
17 "name": "name",
18 "type": "String"
19 }
20 ],
21 "relationships": [
22 {
23 "name": "skipper",
24 "inverse": "sailboat",
25 "inverseClass": "Skipper",
26 "type": "OneToOne",
27 "fetchPolicy": "None",
28 "cascadePolicy": "All"
29 }
30 ],
31 "indexes": [],
32 "expanded": false,
33 "isEditing": false
34 },
35 {
36 "name": "Skipper",
37 "identifier": {
38 "name": "id",
39 "type": "Long",
40 "generator": "Sequence"
41 },
42 "partition": "",
43 "attributes": [
44 {
45 "name": "id",
46 "type": "Long"
47 },
48 {
49 "name": "firstName",
50 "type": "String"
51 },
52 {
53 "name": "lastName",
54 "type": "String"
55 }
56 ],
57 "relationships": [
58 {
59 "name": "sailboat",
60 "inverse": "skipper",
61 "inverseClass": "Sailboat",
62 "type": "OneToOne",
63 "fetchPolicy": "None",
64 "cascadePolicy": "None"
65 }
66 ],
67 "indexes": [],
68 "expanded": false,
69 "isEditing": false
70 }
71 ],
72 "revisionDescription": "Initial schema for one-to-one relationship example"
73 }
- Table names and attributes must follow valid Kotlin variable name conventions (e.g., start with a letter or underscore and only contain alphanumeric characters, underscores, or dollar signs).
- Each table must have a unique name and at least one attribute defined.
- The
identifier
attribute for a table must exist in the attributes list and match its type requirements (e.g., sequence generators are only valid for integer types, and UUID generators are valid only for strings). - Relationships must have a valid name and specify both an
inverse
property andinverseClass
that refer to the corresponding related table and attribute. - To-one relationships (e.g.,
OneToOne
,ManyToOne
) must have a corresponding inverse relationship defined in the related table. - Relationship types must align with the expected pairing (e.g.,
OneToMany
should matchManyToOne
, andManyToMany
must be reciprocal). - Attributes, indexes, and relationships within a table must have unique names and valid configurations.
- Cascade and fetch policies must be explicitly defined and must use supported values (
None
,All
,Save
,Delete
for cascade;None
,Lazy
,Eager
for fetch).
2
Create the Entities and Their Relationship
Create instances of your entities and define their relationships.
1// Create a new sailboat named Wind Passer with Registration Code NCC1701
2const sailboat = new Sailboat({
3 registrationCode: "NCC1701",
4 name: "Wind Passer"
5});
6
7// Create a new Skipper named Martha McFly
8const sailboatSkipper = new Skipper({
9 firstName: "Martha",
10 lastName: "McScuzzy"
11});
12
13// Define the relationship
14sailboat.skipper = sailboatSkipper;
- Assign the related entity to establish the one-to-one relationship.
3
Persist the Entity
Use the Persistence Manager to save your entity.
1await db.save('Sailboat', sailboat);
2console.log(`Created a new Sailboat ${sailboatSkipper.sailboat?.name} with Skipper ${sailboatSkipper.firstName}`);
- The related entity will be automatically persisted due to the
cascadePolicy
.
4
Fetch the Newly Created Entity
Retrieve the saved entity to confirm it has been persisted correctly.
1const newlyCreatedSailboat = await db.findById('Sailboat', 'NCC1701');
2console.log(`Sailboat ${newlyCreatedSailboat?.name} was created with Skipper ${newlyCreatedSailboat?.skipper?.firstName}`);
- Verify that the related entity is properly loaded.
Important Notes
- Onyx does not differentiate between a parent or a child entity; the cascade policy determines the behavior during persistence.
- Entities do not need to be attached prior to persisting, simplifying the process.
- Ensure that the relationship annotations are correctly specified to prevent runtime errors.
Troubleshooting
- Missing or Invalid inverse: Check that table inverse names match the related table's attribute name and that the inverse class is correctly specified.
- Duplicate Names: Check that table names, attribute names, and relationship names are unique within the schema.
- Relationship Configuration Errors: Ensure all relationships specify a valid
inverseClass
andinverse
, and that the inverse relationship exists in the related table. - Relationship Type Mismatch: Verify that paired relationships use compatible types (e.g.,
OneToMany
withManyToOne
orManyToMany
with itself). - Invalid Cascade or Fetch Policy: Check that the cascade and fetch policies are explicitly defined and use valid values such as
None
,All
, orLazy
. - Schema Validation Errors: Use the provided error messages during validation to locate and fix issues such as missing attributes, invalid types, or misconfigured relationships.
Next Steps
Now that you have learned how to define a one-to-one relationship, you can explore more advanced relationship types: