peneksglazami / cartridge-springdata

Spring Data Tarantool

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Spring Data Tarantool

cartridge-springdata:ubuntu/master Actions Status

The primary goal of the Spring Data project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.

The Spring Data Tarantool project provides Spring Data way of working with Tarantool database spaces. The key features are repository support and different API layers which are common in Spring Data, as well as support for asynchronous approaches baked by the underlying asynchronous Tarantool database driver.

Spring Boot compatibility

spring-data-tarantool Version Spring Boot Version
0.x.x 2.2.x

Tarantool compatibility

spring-data-tarantool Version Tarantool Version
0.x.x 1.10.x, 2.x

References

The Tarantool Database documentation is located at tarantool.io

Feel free to join the Tarantool community chat in Telegram (or its counterpart in Russian) if you have any questions about Tarantool database or Spring Data Tarantool.

Detailed questions can be asked on StackOverflow using the tarantool tag.

Documentation and StackOverflow links will be added in the nearest future.

If you are new to Spring as well as to Spring Data, look for information about Spring projects.

Quick Start

Demo project

Check out a traditional Pet Clinic application implemented using this module.

Maven configuration

Add the Maven dependency:

<dependency>
    <groupId>io.tarantool</groupId>
    <artifactId>spring-data-tarantool</artifactId>
    <version>0.4.2</version>
</dependency>

TarantoolTemplate

TarantoolTemplate is the central support class for Tarantool database operations. It provides:

  • Basic POJO mapping support to and from Tarantool tuples
  • Convenience methods to interact with the store (insert object, update objects, select)
  • Exception translation into Spring's technology agnostic DAO exception hierarchy.

Spring Data repositories

To simplify the creation of data repositories Spring Data Tarantool provides a generic repository programming model. It will automatically create a repository proxy for you that adds implementations of finder methods you specify on an interface.

Only simple CRUD operations including entity ID are supported at the moment (will be fixed soon).

For example, given a Book class with name and author properties, a BookRepository interface can be defined for saving and loading the entities:

@Tuple("books")
public class Book {
    @Id
    private Integer id;

    @Field(name = "unique_key")
    private String uniqueKey;

    @Field(name = "book_name")
    private String name;

    private String author;

    private Integer year;
}
public interface BookRepository extends TarantoolRepository<Book, Long> {
}

The @Tuple annotation allows to specify the space name which schema will be used for forming the data tuples. You can add @Tuple on the @Query annotated method that specifies the space name for a specific stored function call. Also, you can add @Tuple annotation to the repository - it acts as a shortcut for specifying the tuple return format and space name for all custom methods in this repository. The @Field annotations provide custom names for the fields. It is necessary to have at least one field marked with the @Id annotation.

Extending CrudRepository causes CRUD methods being pulled into the interface so that you can easily save and find single entities and collections of them.

Let's assume that you have the tarantool/crud module installed in yor Cartridge application and there is an active 'crud-router' role on your router. Put the following settings in your application properties:

Property name Example value Description
tarantool.host localhost Cartridge router host
tarantool.port 3301 Cartridge router port
tarantool.username admin Default username for API access, may be different
tarantool.password myapp-cluster-cookie Password for API access, see you Cartridge application configuration

Than you can have Spring automatically create a proxy for the repository interface by using the following JavaConfig:

@Configuration
@EnableTarantoolRepositories(basePackageClasses = BookRepository.class)
class ApplicationConfig extends AbstractTarantoolDataConfiguration {

    @Value("${tarantool.host}")
    protected String host;
    @Value("${tarantool.port}")
    protected int port;
    @Value("${tarantool.username}")
    protected String username;
    @Value("${tarantool.password}")
    protected String password;

    @Override
    protected TarantoolServerAddress tarantoolServerAddress() {
        return new TarantoolServerAddress(host, port);
    }

    @Override
    public TarantoolCredentials tarantoolCredentials() {
        return new SimpleTarantoolCredentials(username, password);
    }

    @Override
    public TarantoolClient tarantoolClient(TarantoolClientConfig tarantoolClientConfig,
                                           TarantoolClusterAddressProvider tarantoolClusterAddressProvider) {
        return new ProxyTarantoolTupleClient(super.tarantoolClient(tarantoolClientConfig, tarantoolClusterAddressProvider));
    }
}

This sets up a connection to a local Tarantool instance and enables the detection of Spring Data repositories (through @EnableTarantoolRepositories).

This will find the repository interface and register a proxy object in the container. You can use it as shown below:

@Service
public class MyService {

    private final BookRepository repository;

    @Autowired
    public MyService(BookRepository repository) {
        this.repository = repository;
    }

    public void doWork() {
        Book book = new Book();
        book.setName("Le Petit Prince");
        book.setAuthor("Antoine de Saint-Exupéry");
        Book savedBook = repository.save(book);

        List<Book> allBooks = repository.findAll();
    }
}

Proxy methods in repositories

Consider we need to write a complex query in Lua, working with sharded data in Tarantool Cartridge. In this case we can expose this query as a public API function and map that function on a repository method via the @Query annotation:

public interface BookRepository extends CrudRepository<Book, Long> {
    @Query(function = "find_by_complex_query")
    List<Book> findByYearGreaterThenProxy(Integer year);
}

The corresponding function in on Tarantool Cartridge router may look like (uses the tarantool/crud module):

    local crud = require('crud')
    local fun = require('fun')

    ...

    function find_by_complex_query(year)
        return crud.pairs('books'):filter(function(b) return b[6] and b[6] > year end):totable()
    end

See more examples in the module tests.

Call stored functions in Tarantool instance

You can bind repository methods to calls of the stored functions in the Tarantool instance using the @Query annotation with the stored function name specified in the functionName parameter. For such methods, you can specify the stored function response format so that it will be parsed correctly. The response format may be either an object (and a list of objects) or a tuple (and a list of tuples).

@Data
@Tuple
public class SampleUser {
    private String name;
    private String lastName;
}
public interface SampleUserRepository extends TarantoolRepository<SampleUser, String> {
    @Query(function = "returning_sample_user_object")
    SampleUser returningSampleUserObject(String name);

    @Query(function = "get_predefined_users")
    List<SampleUser> getPredefinedUsers();
}
@Tuple
public class Book {
  @Id
  private Integer id;
  private String name;
}

// Here, the space name is "another_space" for all @Query methods
@Tuple("another_space") 
public interface BookRepository extends TarantoolRepository<Book, Integer> {
    
    // The space name here overrides "another_space" to "test_space" for the current method
    @Tuple("test_space") 
    @Query(function = "do_something")
    Optional<Book> doSomething(Book book);
}

Here the space name is not specified neither in a @Tuple annotation on the repository interface, nor on the entity class, so it will be "book_entity" according to the entity class name, turned into "lower snake case". But for the "doSomething" method the space name will be overridden to "test_space".

@Tuple
public class BookEntity {
  @Id
  private Integer id;
  private String name;
}

public interface BookRepository extends TarantoolRepository<BookEntity, Integer> {
    
    // The space name here overrides to "test_space" for the current method
    @Tuple("test_space") 
    @Query(function = "do_something")
    void doSomething(Book book);
}
function returning_sample_user_object()
    return { name = "John", lastName = "Smith" }
end

function get_predefined_users()
    return { { name = "John", lastName = "Smith" }, { name = "Sam", lastName = "Smith" } }
end

Composite primary key

You can create an entity representing a Tarantool tuple with composite primary index. For this you need to use @TarantoolIdClass annotation on entity to specify the type of id. Also you may mark all 'id' fields in the entity with standard @Id annotation. @Id annotation on properties is optional but It is recommended to use it to make code more clear.

See the example:

public class BookTranslationId {
    private Integer bookId;
    private String language;
    private Integer edition;
}

@Tuple("book_translation")
@TarantoolIdClass(BookTranslationId.class)
public class BookTranslation {
    @Id
    @Field(value = "id")
    private Integer bookId;
    @Id
    private String language;
    @Id
    private Integer edition;
    private String translator;
    private String comments;
}

public interface BookTranslationRepository
        extends TarantoolRepository<BookTranslation, BookTranslationId> {
}

Contributing to Spring Data Tarantool

Contributions and issues are welcome, feel free to add them to this project or offer directly in the Tarantool community chat or on StackOverflow using the tarantool tag.

About

Spring Data Tarantool


Languages

Language:Java 92.3%Language:Lua 7.5%Language:Shell 0.3%