diff --git a/cluster/app/owners.lua b/cluster/app/owners.lua index 5223446..9a05fcd 100644 --- a/cluster/app/owners.lua +++ b/cluster/app/owners.lua @@ -2,24 +2,36 @@ local crud = require('crud') -- OneToMany pattern, the owner can have multiple pets local function find_owners_by_last_name(last_name) - local owners + local join_result, owners, err -- return all if the last_name is not specified if last_name == nil or last_name == "" then - owners = crud.select("owners") + owners, err = crud.select("owners") else - owners = crud.select("owners", {{'=', 'last_name', last_name}}) + owners, err = crud.select("owners", {{'=', 'last_name', last_name}}) end + + if err ~= nil then + return nil, err + end + + join_result = { metadata = owners.metadata, rows = {} } + for _, owner in pairs(owners.rows) do - local pets = crud.select("pets", {{'=', 'owner_id', owner[1]}}) + local pets, err = crud.select("pets", {{'=', 'owner_id', owner[1]}}) + if err ~= nil then + return nil, err + end pets = crud.unflatten_rows(pets.rows, pets.metadata) for _, pet in pairs(pets) do -- this is necessary for correct processing of the nested entity pet_type -- for this Query we don't need pet_type name pet.type_id = { id=pet.type_id } end - owner[8] = pets + local owner_with_pets = owner:totable() + owner_with_pets[8] = pets + join_result.rows[#join_result.rows+1] = owner_with_pets end - return owners + return join_result end -- OneToMany = Owners -> Pets @@ -31,9 +43,13 @@ local function find_owner_by_id(id) for _, pet in pairs(pets) do -- in this case we should know full pet_type local type = crud.get("types", pet.type_id) - pet.type_id = crud.unflatten_rows(type.rows, type.metadata)[1] + if type ~= nil and #type.rows >= 1 then + pet.type_id = crud.unflatten_rows(type.rows, type.metadata)[1] + end end - owner.rows[1][8] = pets + owner.rows[1]:transform(8, 1, pets) + require('log').info('owner: %s', require('json').encode(owner)) + require('log').info('owner.rows[1]: %s', require('json').encode(type(owner.rows[1]))) return owner end diff --git a/cluster/app/pets.lua b/cluster/app/pets.lua index 4a85ac7..f6f0030 100644 --- a/cluster/app/pets.lua +++ b/cluster/app/pets.lua @@ -4,6 +4,9 @@ local uuid = require('uuid') -- OneToOne = Pet -> PetType local function find_pet_by_id(id) local pet = crud.select("pets", {{"=", 'id', id}}) + if pet == nil then + return nil + end local type = crud.get("types", pet.rows[1][4]) pet.rows[1][4] = crud.unflatten_rows(type.rows, type.metadata)[1] return pet diff --git a/cluster/testserver-scm-1.rockspec b/cluster/testserver-scm-1.rockspec index ef5d57c..44b21e4 100644 --- a/cluster/testserver-scm-1.rockspec +++ b/cluster/testserver-scm-1.rockspec @@ -5,14 +5,14 @@ source = { } -- Put any modules your app depends on here dependencies = { - 'tarantool', + 'tarantool >= 2.10.0', 'lua >= 5.1', 'checks == 3.1.0-1', - 'cartridge == 2.5.1-1', - 'metrics == 0.6.0-1', - 'cartridge-cli-extensions == 1.1.0-1', - 'crud == 0.5.0-1', - 'migrations' + 'cartridge == 2.7.4-1', + 'metrics == 0.13.0-1', + 'cartridge-cli-extensions == 1.1.1-1', + 'crud == 0.11.2-1', + 'migrations == 0.4.2-1', } build = { type = 'none'; diff --git a/pom.xml b/pom.xml index 929c352..2154985 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.samples spring-petclinic @@ -10,7 +10,9 @@ org.springframework.boot spring-boot-starter-parent + 2.4.2 + petclinic @@ -23,12 +25,12 @@ 3.3.6 - 1.11.4 - 2.2.4 + 1.13.1 + 3.6.0 1.8.0 - 0.8.5 - 0.0.4.RELEASE + 0.8.8 + 0.0.10 0.0.25 @@ -58,12 +60,12 @@ org.springframework.boot spring-boot-starter-test test - - - org.junit.vintage - junit-vintage-engine - - + + + org.junit.vintage + junit-vintage-engine + + @@ -119,7 +121,7 @@ io.tarantool spring-data-tarantool - 0.3.4 + 0.5.1 diff --git a/readme.md b/readme.md index 1062300..572c561 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ See the presentation here ## Running petclinic locally -First you need to [install](https://www.tarantool.io/en/download/os-installation/ubuntu/) tarantool on the system. Well, then you need to start the tarantool cluster: +First you need to [install](https://www.tarantool.io/en/download/os-installation/) tarantool on the system. Well, then you need to start the tarantool cluster: ``` git clone git@github.com:ArtDu/spring-petclinic-tarantool.git cd spring-petclinic-tarantool/cluster @@ -48,7 +48,7 @@ In the future, the example will be improved to work without limiting versions. Tarantool is Reliable NoSQL DBMS. This example is run using the [cartridge framework](https://www.tarantool.io/en/cartridge/), -which is a handy tarantools orchestrator. +which is a handy tarantools orchestrator. Data sharding is performed on the principle of virtual buckets using the [vshard module](https://github.com/tarantool/vshard). Therefore, to begin with, we must install all the necessary modules, they are described in the `testserver-scm-1.rockspec` file using the command `tarantoolctl rocks make` @@ -91,31 +91,31 @@ The cluster configuration is located in the replicasets.yml file: ```yaml r1: instances: - - router + - router roles: - - vshard-router - - crud-router - - app.roles.api_router + - vshard-router + - crud-router + - app.roles.api_router all_rw: false s1: instances: - - s1-master - - s1-replica + - s1-master + - s1-replica roles: - - vshard-storage - - crud-storage - - app.roles.api_storage + - vshard-storage + - crud-storage + - app.roles.api_storage weight: 1 all_rw: false vshard_group: default s2: instances: - - s2-master - - s2-replica + - s2-master + - s2-replica roles: - - vshard-storage - - crud-storage - - app.roles.api_storage + - vshard-storage + - crud-storage + - app.roles.api_storage weight: 1 all_rw: false vshard_group: default @@ -130,7 +130,7 @@ s2: Next, using the [migration module ](https://github.com/tarantool/migrations) can run cluster-wide migrations for your data `curl -X POST http://localhost:8081/migrations/up` -and fill in the data using a tarantool router connection: +and fill in the data using a tarantool router connection: `echo "require('data')" | tarantoolctl connect admin:secret-cluster-cookie@0.0.0.0:3301` `` @@ -144,15 +144,15 @@ Let's look at the data model: Here we can immediately see that there are no familiar relationships using foreign keys, and the datatype of the primary keys is uuid. To keep data normalization, data splitting and joining are being did in a special way: -1. We use **uuid** as it is much faster than using any auto-increment on the storage cluster. -UUID can be generated both on the client and on the side of tarantool application scripts. +1. We use **uuid** as it is much faster than using any auto-increment on the storage cluster. + UUID can be generated both on the client and on the side of tarantool application scripts. 2. **Join** happens predominantly on storages, if possible, and is aggregated using map reduce. 3. Fields where the data type is indicated with a question mark means that this field can be **nullable**, this is done so that we can return data nested using custom joins. 4. Since there are no foreign keys, **secondary indexes** were added to quickly fetch data. In the diagram, they are indicated by a graph tree. ### Data nesting and joining -You can nest data from one space into another. To do this, you need to place an empty field in space_object: format. When transferring data from the database to the client, add data to this stub. +You can nest data from one space into another. To do this, you need to place an empty field in space_object: format. When transferring data from the database to the client, add data to this stub. E.g.: ```lua owners:format({ @@ -230,7 +230,7 @@ end ## Work from java client -Working with tarantool is done using the tarantool function binding via Query annotation, which is indicated above, or use the standard CrudRepository functions (findById, save and etc). +Working with tarantool is done using the tarantool function binding via Query annotation, which is indicated above, or use the standard CrudRepository functions (findById, save and etc). ```java @@ -239,7 +239,7 @@ Working with tarantool is done using the tarantool function binding via Query an Owner findOwnerById(UUID id); // Using default CrudRepository operations -Owner save(Owner owner); + Owner save(Owner owner); ``` Data mapping from tarantool spaces to java entities is implemented with @tuple and @field annotations. See [tarantool-springdata]((https://github.com/tarantool/cartridge-springdata)) module for details. @@ -262,7 +262,7 @@ Data is obtained from cluster via custom functions lua functions or '[crud](http ```java @Configuration @EnableTarantoolRepositories(basePackageClasses = { VetRepository.class, OwnerRepository.class, PetRepository.class, - PetTypeRepository.class, VisitRepository.class }) + PetTypeRepository.class, VisitRepository.class }) public class TarantoolConfiguration extends AbstractTarantoolDataConfiguration { // localhost diff --git a/src/main/java/org/springframework/samples/petclinic/tarantool/TarantoolConfiguration.java b/src/main/java/org/springframework/samples/petclinic/tarantool/TarantoolConfiguration.java index 0888d75..8ee79b7 100644 --- a/src/main/java/org/springframework/samples/petclinic/tarantool/TarantoolConfiguration.java +++ b/src/main/java/org/springframework/samples/petclinic/tarantool/TarantoolConfiguration.java @@ -1,11 +1,11 @@ package org.springframework.samples.petclinic.tarantool; import io.tarantool.driver.*; -import io.tarantool.driver.api.TarantoolClient; -import io.tarantool.driver.api.TarantoolResult; +import io.tarantool.driver.api.*; import io.tarantool.driver.api.tuple.TarantoolTuple; import io.tarantool.driver.auth.SimpleTarantoolCredentials; import io.tarantool.driver.auth.TarantoolCredentials; +import io.tarantool.driver.core.ProxyTarantoolTupleClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.data.tarantool.config.AbstractTarantoolDataConfiguration;