diff --git a/changelog.md b/changelog.md index aa0d5c723..8b8a545b7 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - [Fixed the issue related to existing configurations in Config.toml file](https://github.com/ballerina-platform/persist-tools/issues/314) +- [Fix the logic in persist client generation with respect to refColumns in the joinMetadata](https://github.com/ballerina-platform/persist-tools/issues/312) ## [1.2.0] - 2023-09-19 @@ -25,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Updated error messages to be consistent across all data sources](https://github.com/ballerina-platform/ballerina-standard-library/issues/4360) - [Removed constraint ID from foreign keys in generated SQL scripts](https://github.com/ballerina-platform/ballerina-standard-library/issues/4581) + ## [1.0.0] - 2021-06-01 ### Added diff --git a/persist-cli-tests/src/test/java/io/ballerina/persist/tools/ToolingGenerateTest.java b/persist-cli-tests/src/test/java/io/ballerina/persist/tools/ToolingGenerateTest.java index 8f004a7ed..ae98ea365 100644 --- a/persist-cli-tests/src/test/java/io/ballerina/persist/tools/ToolingGenerateTest.java +++ b/persist-cli-tests/src/test/java/io/ballerina/persist/tools/ToolingGenerateTest.java @@ -446,4 +446,11 @@ public void testGenerateEntityDotSeperatedModuleNames() { executeCommand("tool_test_generate_64", GENERATE); assertGeneratedSources("tool_test_generate_64"); } + + @Test(enabled = true) + @Description("The model has multiple relations of various types") + public void testGenerateEntitiesWithMultipleRelations() { + executeCommand("tool_test_generate_65", GENERATE); + assertGeneratedSources("tool_test_generate_65"); + } } diff --git a/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/Ballerina.toml b/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/Ballerina.toml new file mode 100644 index 000000000..0b7fa8d00 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/Ballerina.toml @@ -0,0 +1,12 @@ +[package] +org = "foo" +name = "persist_generate_65" +version = "0.1.0" +distribution = "2201.3.0" + +[build-options] +observabilityIncluded = false + +[persist] +datastore = "mysql" +module = "persist_generate_65" diff --git a/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/main.bal b/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/main.bal new file mode 100644 index 000000000..d471c43a4 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/main.bal @@ -0,0 +1,21 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; + +public function main() { + io:println("hello"); +} diff --git a/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/persist/model.bal b/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/persist/model.bal new file mode 100644 index 000000000..d30b4942f --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/input/tool_test_generate_65/persist/model.bal @@ -0,0 +1,50 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/persist as _; + +type Book record {| + readonly string bookId; + string title; + string author; + decimal price; + int stock; + OrderItem? orderitem; +|}; + +type Order record {| + readonly string orderId; + string customerId; + string createdAt; + decimal totalPrice; + OrderItem[] orderItems; + Payment? payment; +|}; + +type OrderItem record {| + readonly string orderItemId; + int quantity; + decimal price; + Book book; + Order 'order; +|}; + +type Payment record {| + readonly string paymentId; + decimal paymentAmount; + string paymentDate; + Order 'order; +|}; diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_1/generated/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_1/generated/persist_client.bal index c089247a3..8036ac056 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_1/generated/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_1/generated/persist_client.bal @@ -42,7 +42,7 @@ public isolated client class Client { keyFields: ["empNo"], joinMetadata: { department: {entity: Department, fieldName: "department", refTable: "Department", refColumns: ["deptNo"], joinColumns: ["departmentDeptNo"], 'type: psql:ONE_TO_MANY}, - workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["employeeEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} + workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["workspaceEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} } }, [WORKSPACE] : { diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_36/generated/rainier/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_36/generated/rainier/persist_client.bal index be76e88aa..8ae99e44b 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_36/generated/rainier/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_36/generated/rainier/persist_client.bal @@ -37,7 +37,7 @@ public isolated client class Client { "workspaces[].locationBuildingCode": {relation: {entityName: "workspaces", refField: "locationBuildingCode"}} }, keyFields: ["buildingCode"], - joinMetadata: {workspaces: {entity: 'Workspace, fieldName: "workspaces", refTable: "'Workspace", refColumns: ["locationBuildingCode"], joinColumns: ["'buildingCode"], 'type: psql:MANY_TO_ONE}} + joinMetadata: {workspaces: {entity: 'Workspace, fieldName: "workspaces", refTable: "'Workspace", refColumns: ["locationBuildingCode"], joinColumns: ["buildingCode"], 'type: psql:MANY_TO_ONE}} }, [DEPARTMENT] : { entityName: "Department", @@ -116,7 +116,7 @@ public isolated client class Client { }, keyFields: ["workspaceId"], joinMetadata: { - location: {entity: 'Building, fieldName: "location", refTable: "'Building", refColumns: ["'buildingCode"], joinColumns: ["locationBuildingCode"], 'type: psql:ONE_TO_MANY}, + location: {entity: 'Building, fieldName: "location", refTable: "'Building", refColumns: ["buildingCode"], joinColumns: ["locationBuildingCode"], 'type: psql:ONE_TO_MANY}, employee: {entity: 'Employee, fieldName: "employee", refTable: "'Employee", refColumns: ["workspaceWorkspaceId"], joinColumns: ["workspaceId"], 'type: psql:ONE_TO_ONE} } } diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_50/generated/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_50/generated/persist_client.bal index 092698903..7694476cd 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_50/generated/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_50/generated/persist_client.bal @@ -40,7 +40,7 @@ public isolated client class Client { keyFields: ["empNo"], joinMetadata: { department: {entity: Department, fieldName: "department", refTable: "Department", refColumns: ["deptNo"], joinColumns: ["departmentDeptNo"], 'type: psql:ONE_TO_MANY}, - workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["employeeEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} + workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["workspaceEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} } }, [WORKSPACE] : { diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_51/generated/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_51/generated/persist_client.bal index 49bd52210..9dcaec916 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_51/generated/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_51/generated/persist_client.bal @@ -42,7 +42,7 @@ public isolated client class Client { keyFields: ["empNo"], joinMetadata: { department: {entity: Department, fieldName: "department", refTable: "Department", refColumns: ["deptNo"], joinColumns: ["departmentDeptNo"], 'type: psql:ONE_TO_MANY}, - workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["employeeEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} + workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["workspaceEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} } }, [WORKSPACE] : { diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_61_mssql/generated/entities/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_61_mssql/generated/entities/persist_client.bal index ad42f9858..6ce949566 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_61_mssql/generated/entities/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_61_mssql/generated/entities/persist_client.bal @@ -42,7 +42,7 @@ public isolated client class Client { keyFields: ["empNo"], joinMetadata: { department: {entity: Department, fieldName: "department", refTable: "Department", refColumns: ["deptNo"], joinColumns: ["departmentDeptNo"], 'type: psql:ONE_TO_MANY}, - workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["employeeEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} + workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["workspaceEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} } }, [WORKSPACE] : { diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_62_mssql/generated/entities/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_62_mssql/generated/entities/persist_client.bal index 470ddd9a4..f89b0b14f 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_62_mssql/generated/entities/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_62_mssql/generated/entities/persist_client.bal @@ -46,8 +46,8 @@ public isolated client class Client { keyFields: ["id"], joinMetadata: { posts: {entity: Post, fieldName: "posts", refTable: "Post", refColumns: ["userId"], joinColumns: ["id"], 'type: psql:MANY_TO_ONE}, - leader: {entity: Follow, fieldName: "leader", refTable: "Follow", refColumns: ["userId"], joinColumns: ["id"], 'type: psql:ONE_TO_ONE}, - follower: {entity: Follow, fieldName: "follower", refTable: "Follow", refColumns: ["userId"], joinColumns: ["id"], 'type: psql:ONE_TO_ONE} + leader: {entity: Follow, fieldName: "leader", refTable: "Follow", refColumns: ["leaderId"], joinColumns: ["id"], 'type: psql:ONE_TO_ONE}, + follower: {entity: Follow, fieldName: "follower", refTable: "Follow", refColumns: ["followerId"], joinColumns: ["id"], 'type: psql:ONE_TO_ONE} } }, [POST] : { diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_64/generated/x.y.z/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_64/generated/x.y.z/persist_client.bal index c089247a3..8036ac056 100644 --- a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_64/generated/x.y.z/persist_client.bal +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_64/generated/x.y.z/persist_client.bal @@ -42,7 +42,7 @@ public isolated client class Client { keyFields: ["empNo"], joinMetadata: { department: {entity: Department, fieldName: "department", refTable: "Department", refColumns: ["deptNo"], joinColumns: ["departmentDeptNo"], 'type: psql:ONE_TO_MANY}, - workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["employeeEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} + workspace: {entity: Workspace, fieldName: "workspace", refTable: "Workspace", refColumns: ["workspaceEmpNo"], joinColumns: ["empNo"], 'type: psql:ONE_TO_ONE} } }, [WORKSPACE] : { diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/Ballerina.toml b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/Ballerina.toml new file mode 100644 index 000000000..0b7fa8d00 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/Ballerina.toml @@ -0,0 +1,12 @@ +[package] +org = "foo" +name = "persist_generate_65" +version = "0.1.0" +distribution = "2201.3.0" + +[build-options] +observabilityIncluded = false + +[persist] +datastore = "mysql" +module = "persist_generate_65" diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/Config.toml b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/Config.toml new file mode 100644 index 000000000..9449e5337 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/Config.toml @@ -0,0 +1,7 @@ +[persist_generate_65] +host = "localhost" +port = 3306 +user = "root" +password = "" +database = "" + diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_client.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_client.bal new file mode 100644 index 000000000..44442a1c7 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_client.bal @@ -0,0 +1,295 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// This file is an auto-generated file by Ballerina persistence layer for model. +// It should not be modified by hand. +import ballerina/jballerina.java; +import ballerina/persist; +import ballerina/sql; +import ballerinax/mysql; +import ballerinax/mysql.driver as _; +import ballerinax/persist.sql as psql; + +const BOOK = "books"; +const ORDER = "orders"; +const ORDER_ITEM = "orderitems"; +const PAYMENT = "payments"; + +public isolated client class Client { + *persist:AbstractPersistClient; + + private final mysql:Client dbClient; + + private final map persistClients; + + private final record {|psql:SQLMetadata...;|} & readonly metadata = { + [BOOK] : { + entityName: "Book", + tableName: "Book", + fieldMetadata: { + bookId: {columnName: "bookId"}, + title: {columnName: "title"}, + author: {columnName: "author"}, + price: {columnName: "price"}, + stock: {columnName: "stock"}, + "orderitem.orderItemId": {relation: {entityName: "orderitem", refField: "orderItemId"}}, + "orderitem.quantity": {relation: {entityName: "orderitem", refField: "quantity"}}, + "orderitem.price": {relation: {entityName: "orderitem", refField: "price"}}, + "orderitem.orderitemBookId": {relation: {entityName: "orderitem", refField: "orderitemBookId"}}, + "orderitem.orderOrderId": {relation: {entityName: "orderitem", refField: "orderOrderId"}} + }, + keyFields: ["bookId"], + joinMetadata: {orderitem: {entity: OrderItem, fieldName: "orderitem", refTable: "OrderItem", refColumns: ["orderitemBookId"], joinColumns: ["bookId"], 'type: psql:ONE_TO_ONE}} + }, + [ORDER] : { + entityName: "Order", + tableName: "Order", + fieldMetadata: { + orderId: {columnName: "orderId"}, + customerId: {columnName: "customerId"}, + createdAt: {columnName: "createdAt"}, + totalPrice: {columnName: "totalPrice"}, + "orderItems[].orderItemId": {relation: {entityName: "orderItems", refField: "orderItemId"}}, + "orderItems[].quantity": {relation: {entityName: "orderItems", refField: "quantity"}}, + "orderItems[].price": {relation: {entityName: "orderItems", refField: "price"}}, + "orderItems[].orderitemBookId": {relation: {entityName: "orderItems", refField: "orderitemBookId"}}, + "orderItems[].orderOrderId": {relation: {entityName: "orderItems", refField: "orderOrderId"}}, + "payment.paymentId": {relation: {entityName: "payment", refField: "paymentId"}}, + "payment.paymentAmount": {relation: {entityName: "payment", refField: "paymentAmount"}}, + "payment.paymentDate": {relation: {entityName: "payment", refField: "paymentDate"}}, + "payment.paymentOrderId": {relation: {entityName: "payment", refField: "paymentOrderId"}} + }, + keyFields: ["orderId"], + joinMetadata: { + orderItems: {entity: OrderItem, fieldName: "orderItems", refTable: "OrderItem", refColumns: ["orderOrderId"], joinColumns: ["orderId"], 'type: psql:MANY_TO_ONE}, + payment: {entity: Payment, fieldName: "payment", refTable: "Payment", refColumns: ["paymentOrderId"], joinColumns: ["orderId"], 'type: psql:ONE_TO_ONE} + } + }, + [ORDER_ITEM] : { + entityName: "OrderItem", + tableName: "OrderItem", + fieldMetadata: { + orderItemId: {columnName: "orderItemId"}, + quantity: {columnName: "quantity"}, + price: {columnName: "price"}, + orderitemBookId: {columnName: "orderitemBookId"}, + orderOrderId: {columnName: "orderOrderId"}, + "book.bookId": {relation: {entityName: "book", refField: "bookId"}}, + "book.title": {relation: {entityName: "book", refField: "title"}}, + "book.author": {relation: {entityName: "book", refField: "author"}}, + "book.price": {relation: {entityName: "book", refField: "price"}}, + "book.stock": {relation: {entityName: "book", refField: "stock"}}, + "order.orderId": {relation: {entityName: "order", refField: "orderId"}}, + "order.customerId": {relation: {entityName: "order", refField: "customerId"}}, + "order.createdAt": {relation: {entityName: "order", refField: "createdAt"}}, + "order.totalPrice": {relation: {entityName: "order", refField: "totalPrice"}} + }, + keyFields: ["orderItemId"], + joinMetadata: { + book: {entity: Book, fieldName: "book", refTable: "Book", refColumns: ["bookId"], joinColumns: ["orderitemBookId"], 'type: psql:ONE_TO_ONE}, + 'order: {entity: Order, fieldName: "'order", refTable: "Order", refColumns: ["orderId"], joinColumns: ["orderOrderId"], 'type: psql:ONE_TO_MANY} + } + }, + [PAYMENT] : { + entityName: "Payment", + tableName: "Payment", + fieldMetadata: { + paymentId: {columnName: "paymentId"}, + paymentAmount: {columnName: "paymentAmount"}, + paymentDate: {columnName: "paymentDate"}, + paymentOrderId: {columnName: "paymentOrderId"}, + "order.orderId": {relation: {entityName: "order", refField: "orderId"}}, + "order.customerId": {relation: {entityName: "order", refField: "customerId"}}, + "order.createdAt": {relation: {entityName: "order", refField: "createdAt"}}, + "order.totalPrice": {relation: {entityName: "order", refField: "totalPrice"}} + }, + keyFields: ["paymentId"], + joinMetadata: {'order: {entity: Order, fieldName: "'order", refTable: "Order", refColumns: ["orderId"], joinColumns: ["paymentOrderId"], 'type: psql:ONE_TO_ONE}} + } + }; + + public isolated function init() returns persist:Error? { + mysql:Client|error dbClient = new (host = host, user = user, password = password, database = database, port = port, options = connectionOptions); + if dbClient is error { + return error(dbClient.message()); + } + self.dbClient = dbClient; + self.persistClients = { + [BOOK] : check new (dbClient, self.metadata.get(BOOK), psql:MYSQL_SPECIFICS), + [ORDER] : check new (dbClient, self.metadata.get(ORDER), psql:MYSQL_SPECIFICS), + [ORDER_ITEM] : check new (dbClient, self.metadata.get(ORDER_ITEM), psql:MYSQL_SPECIFICS), + [PAYMENT] : check new (dbClient, self.metadata.get(PAYMENT), psql:MYSQL_SPECIFICS) + }; + } + + isolated resource function get books(BookTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "query" + } external; + + isolated resource function get books/[string bookId](BookTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "queryOne" + } external; + + isolated resource function post books(BookInsert[] data) returns string[]|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(BOOK); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from BookInsert inserted in data + select inserted.bookId; + } + + isolated resource function put books/[string bookId](BookUpdate value) returns Book|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(BOOK); + } + _ = check sqlClient.runUpdateQuery(bookId, value); + return self->/books/[bookId].get(); + } + + isolated resource function delete books/[string bookId]() returns Book|persist:Error { + Book result = check self->/books/[bookId].get(); + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(BOOK); + } + _ = check sqlClient.runDeleteQuery(bookId); + return result; + } + + isolated resource function get orders(OrderTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "query" + } external; + + isolated resource function get orders/[string orderId](OrderTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "queryOne" + } external; + + isolated resource function post orders(OrderInsert[] data) returns string[]|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(ORDER); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from OrderInsert inserted in data + select inserted.orderId; + } + + isolated resource function put orders/[string orderId](OrderUpdate value) returns Order|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(ORDER); + } + _ = check sqlClient.runUpdateQuery(orderId, value); + return self->/orders/[orderId].get(); + } + + isolated resource function delete orders/[string orderId]() returns Order|persist:Error { + Order result = check self->/orders/[orderId].get(); + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(ORDER); + } + _ = check sqlClient.runDeleteQuery(orderId); + return result; + } + + isolated resource function get orderitems(OrderItemTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "query" + } external; + + isolated resource function get orderitems/[string orderItemId](OrderItemTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "queryOne" + } external; + + isolated resource function post orderitems(OrderItemInsert[] data) returns string[]|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(ORDER_ITEM); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from OrderItemInsert inserted in data + select inserted.orderItemId; + } + + isolated resource function put orderitems/[string orderItemId](OrderItemUpdate value) returns OrderItem|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(ORDER_ITEM); + } + _ = check sqlClient.runUpdateQuery(orderItemId, value); + return self->/orderitems/[orderItemId].get(); + } + + isolated resource function delete orderitems/[string orderItemId]() returns OrderItem|persist:Error { + OrderItem result = check self->/orderitems/[orderItemId].get(); + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(ORDER_ITEM); + } + _ = check sqlClient.runDeleteQuery(orderItemId); + return result; + } + + isolated resource function get payments(PaymentTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "query" + } external; + + isolated resource function get payments/[string paymentId](PaymentTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "queryOne" + } external; + + isolated resource function post payments(PaymentInsert[] data) returns string[]|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(PAYMENT); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from PaymentInsert inserted in data + select inserted.paymentId; + } + + isolated resource function put payments/[string paymentId](PaymentUpdate value) returns Payment|persist:Error { + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(PAYMENT); + } + _ = check sqlClient.runUpdateQuery(paymentId, value); + return self->/payments/[paymentId].get(); + } + + isolated resource function delete payments/[string paymentId]() returns Payment|persist:Error { + Payment result = check self->/payments/[paymentId].get(); + psql:SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(PAYMENT); + } + _ = check sqlClient.runDeleteQuery(paymentId); + return result; + } + + remote isolated function queryNativeSQL(sql:ParameterizedQuery sqlQuery, typedesc rowType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor" + } external; + + remote isolated function executeNativeSQL(sql:ParameterizedQuery sqlQuery) returns psql:ExecutionResult|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor" + } external; + + public isolated function close() returns persist:Error? { + error? result = self.dbClient.close(); + if result is error { + return error(result.message()); + } + return result; + } +} + diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_db_config.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_db_config.bal new file mode 100644 index 000000000..aedfe37e0 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_db_config.bal @@ -0,0 +1,12 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// This file is an auto-generated file by Ballerina persistence layer. +// It should not be modified by hand. +import ballerinax/mysql; + +configurable int port = ?; +configurable string host = ?; +configurable string user = ?; +configurable string database = ?; +configurable string password = ?; +configurable mysql:Options & readonly connectionOptions = {}; + diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_types.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_types.bal new file mode 100644 index 000000000..9ef4bc476 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/persist_types.bal @@ -0,0 +1,129 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. + +// This file is an auto-generated file by Ballerina persistence layer for model. +// It should not be modified by hand. + +public type Book record {| + readonly string bookId; + string title; + string author; + decimal price; + int stock; +|}; + +public type BookOptionalized record {| + string bookId?; + string title?; + string author?; + decimal price?; + int stock?; +|}; + +public type BookWithRelations record {| + *BookOptionalized; + OrderItemOptionalized orderitem?; +|}; + +public type BookTargetType typedesc; + +public type BookInsert Book; + +public type BookUpdate record {| + string title?; + string author?; + decimal price?; + int stock?; +|}; + +public type Order record {| + readonly string orderId; + string customerId; + string createdAt; + decimal totalPrice; +|}; + +public type OrderOptionalized record {| + string orderId?; + string customerId?; + string createdAt?; + decimal totalPrice?; +|}; + +public type OrderWithRelations record {| + *OrderOptionalized; + OrderItemOptionalized[] orderItems?; + PaymentOptionalized payment?; +|}; + +public type OrderTargetType typedesc; + +public type OrderInsert Order; + +public type OrderUpdate record {| + string customerId?; + string createdAt?; + decimal totalPrice?; +|}; + +public type OrderItem record {| + readonly string orderItemId; + int quantity; + decimal price; + string orderitemBookId; + string orderOrderId; +|}; + +public type OrderItemOptionalized record {| + string orderItemId?; + int quantity?; + decimal price?; + string orderitemBookId?; + string orderOrderId?; +|}; + +public type OrderItemWithRelations record {| + *OrderItemOptionalized; + BookOptionalized book?; + OrderOptionalized 'order?; +|}; + +public type OrderItemTargetType typedesc; + +public type OrderItemInsert OrderItem; + +public type OrderItemUpdate record {| + int quantity?; + decimal price?; + string orderitemBookId?; + string orderOrderId?; +|}; + +public type Payment record {| + readonly string paymentId; + decimal paymentAmount; + string paymentDate; + string paymentOrderId; +|}; + +public type PaymentOptionalized record {| + string paymentId?; + decimal paymentAmount?; + string paymentDate?; + string paymentOrderId?; +|}; + +public type PaymentWithRelations record {| + *PaymentOptionalized; + OrderOptionalized 'order?; +|}; + +public type PaymentTargetType typedesc; + +public type PaymentInsert Payment; + +public type PaymentUpdate record {| + decimal paymentAmount?; + string paymentDate?; + string paymentOrderId?; +|}; + diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/script.sql b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/script.sql new file mode 100644 index 000000000..285bb82c8 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/generated/script.sql @@ -0,0 +1,46 @@ +-- AUTO-GENERATED FILE. + +-- This file is an auto-generated file by Ballerina persistence layer for model. +-- Please verify the generated scripts and execute them against the target DB server. + +DROP TABLE IF EXISTS `Payment`; +DROP TABLE IF EXISTS `OrderItem`; +DROP TABLE IF EXISTS `Order`; +DROP TABLE IF EXISTS `Book`; + +CREATE TABLE `Book` ( + `bookId` VARCHAR(191) NOT NULL, + `title` VARCHAR(191) NOT NULL, + `author` VARCHAR(191) NOT NULL, + `price` DECIMAL(65,30) NOT NULL, + `stock` INT NOT NULL, + PRIMARY KEY(`bookId`) +); + +CREATE TABLE `Order` ( + `orderId` VARCHAR(191) NOT NULL, + `customerId` VARCHAR(191) NOT NULL, + `createdAt` VARCHAR(191) NOT NULL, + `totalPrice` DECIMAL(65,30) NOT NULL, + PRIMARY KEY(`orderId`) +); + +CREATE TABLE `OrderItem` ( + `orderItemId` VARCHAR(191) NOT NULL, + `quantity` INT NOT NULL, + `price` DECIMAL(65,30) NOT NULL, + `orderitemBookId` VARCHAR(191) UNIQUE NOT NULL, + FOREIGN KEY(`orderitemBookId`) REFERENCES `Book`(`bookId`), + `orderOrderId` VARCHAR(191) NOT NULL, + FOREIGN KEY(`orderOrderId`) REFERENCES `Order`(`orderId`), + PRIMARY KEY(`orderItemId`) +); + +CREATE TABLE `Payment` ( + `paymentId` VARCHAR(191) NOT NULL, + `paymentAmount` DECIMAL(65,30) NOT NULL, + `paymentDate` VARCHAR(191) NOT NULL, + `paymentOrderId` VARCHAR(191) UNIQUE NOT NULL, + FOREIGN KEY(`paymentOrderId`) REFERENCES `Order`(`orderId`), + PRIMARY KEY(`paymentId`) +); diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/main.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/main.bal new file mode 100644 index 000000000..d471c43a4 --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/main.bal @@ -0,0 +1,21 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; + +public function main() { + io:println("hello"); +} diff --git a/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/persist/model.bal b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/persist/model.bal new file mode 100644 index 000000000..d30b4942f --- /dev/null +++ b/persist-cli-tests/src/test/resources/test-src/output/tool_test_generate_65/persist/model.bal @@ -0,0 +1,50 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/persist as _; + +type Book record {| + readonly string bookId; + string title; + string author; + decimal price; + int stock; + OrderItem? orderitem; +|}; + +type Order record {| + readonly string orderId; + string customerId; + string createdAt; + decimal totalPrice; + OrderItem[] orderItems; + Payment? payment; +|}; + +type OrderItem record {| + readonly string orderItemId; + int quantity; + decimal price; + Book book; + Order 'order; +|}; + +type Payment record {| + readonly string paymentId; + decimal paymentAmount; + string paymentDate; + Order 'order; +|}; diff --git a/persist-cli/src/main/java/io/ballerina/persist/nodegenerator/syntax/clients/DbClientSyntax.java b/persist-cli/src/main/java/io/ballerina/persist/nodegenerator/syntax/clients/DbClientSyntax.java index 64f09dbb1..54ff5baae 100644 --- a/persist-cli/src/main/java/io/ballerina/persist/nodegenerator/syntax/clients/DbClientSyntax.java +++ b/persist-cli/src/main/java/io/ballerina/persist/nodegenerator/syntax/clients/DbClientSyntax.java @@ -365,8 +365,9 @@ private static String getJoinMetaData(Entity entity) { refColumns.append(BalSyntaxConstants.COMMA); } refColumns.append(String.format(BalSyntaxConstants.COLUMN_ARRAY_ENTRY_TEMPLATE, - key.getReference())); - joinColumns.append(String.format(BalSyntaxConstants.COLUMN_ARRAY_ENTRY_TEMPLATE, key.getField())); + BalSyntaxUtils.stripEscapeCharacter(key.getReference()))); + joinColumns.append(String.format(BalSyntaxConstants.COLUMN_ARRAY_ENTRY_TEMPLATE, + BalSyntaxUtils.stripEscapeCharacter(key.getField()))); } joinMetaData.append(String.format(BalSyntaxConstants.JOIN_METADATA_FIELD_TEMPLATE, entityField.getFieldName(), entityField.getFieldType(), diff --git a/persist-cli/src/main/java/io/ballerina/persist/utils/BalProjectUtils.java b/persist-cli/src/main/java/io/ballerina/persist/utils/BalProjectUtils.java index 4383a7481..5b9f0a8a5 100644 --- a/persist-cli/src/main/java/io/ballerina/persist/utils/BalProjectUtils.java +++ b/persist-cli/src/main/java/io/ballerina/persist/utils/BalProjectUtils.java @@ -351,53 +351,6 @@ public static void inferRelationDetails(Module entityModule) { } } }); - } else if (field.getRelation() != null && field.getRelation().isOwner()) { - field.getRelation().setRelationType( - field.isArrayType() ? Relation.RelationType.MANY : Relation.RelationType.ONE); - field.getRelation().setAssocEntity(assocEntity); - List keyColumns = field.getRelation().getKeyColumns(); - if (keyColumns == null || keyColumns.size() == 0) { - keyColumns = assocEntity.getKeys().stream() - .map(key -> new Relation.Key( - assocEntity.getEntityName().toLowerCase(Locale.ENGLISH) - + stripEscapeCharacter(key.getFieldName()).substring(0, 1) - .toUpperCase(Locale.ENGLISH) - + stripEscapeCharacter(key.getFieldName()).substring(1), - key.getFieldName(), key.getFieldType())) - .collect(Collectors.toList()); - field.getRelation().setKeyColumns(keyColumns); - } - List references = field.getRelation().getReferences(); - if (references == null || references.size() == 0) { - field.getRelation().setReferences(assocEntity.getKeys().stream() - .map(EntityField::getFieldName) - .collect(Collectors.toList())); - } - - // create bidirectional mapping for associated entity - Relation.Builder assocRelBuilder = Relation.newBuilder(); - assocRelBuilder.setOwner(false); - assocRelBuilder.setAssocEntity(entity); - - List assockeyColumns = assocEntity - .getKeys().stream().map(key -> new Relation.Key(key.getFieldName(), - assocEntity.getEntityName().toLowerCase(Locale.ENGLISH) - + stripEscapeCharacter(key.getFieldName()).substring(0, 1) - .toUpperCase(Locale.ENGLISH) - + stripEscapeCharacter(key.getFieldName()).substring(1), - key.getFieldType())) - .collect(Collectors.toList()); - assocRelBuilder.setKeys(assockeyColumns); - assocRelBuilder.setReferences(assockeyColumns.stream().map(Relation.Key::getReference) - .collect(Collectors.toList())); - assocEntity.getFields().stream().filter(assocfield -> assocfield.getFieldType() - .equals(entity.getEntityName())).forEach( - assocField -> { - assocRelBuilder.setRelationType( - assocField.isArrayType() ? Relation.RelationType.MANY - : Relation.RelationType.ONE); - assocField.setRelation(assocRelBuilder.build()); - }); } }); }