Repositories and entities

In the previous chapter we investigated some basics of creating and using entities. However, the examples so far were leaving the entities static - without persistence with a database.

Prepare the database

Lets create a database table for our MusicLib::Song entity:

USE `glagol`;

CREATE TABLE `songs` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(45) NULL,
  `genre` VARCHAR(45) NULL,
  `author` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

Attention

If you are using the Docker Compose default project structure you can just put the SQL snippet displayed above in the ./database/data.sql file. The database folder is imported by the MySQL docker container upon build - just rebuild your stack and the table will be created and ready to use. Additionally, you can tweak the database name the app is using by changing the DB_DATABASE environmental variable from docker-compose.yml. The database name for any Glagol DSL app defaults to glagol.

Annotating the entity

Next step is to tell Glagol DSL that the Song entity related to the glagol.songs database table.

First, add the @table annotation just before the declaration of the entity:

namespace MusicLib

@table="songs"
entity Song {
    int id;
    string title;
    string genre;
    string author;
}

After you compile the sources the environment will know that the Song entity is bundled with the glagol.songs table.

Secondly, we need to annotate the primary key field and flag it as auto-incremented. Simply add the @id and @sequence annotations just before the int id; property declaration:

namespace MusicLib

@table="songs"
entity Song {
    @id
    @sequence
    int id;
    string title;
    string genre;
    string author;
}

The @id annotation will tell Glagol DSL that the targeted field is the primary key field. As for the @sequence annotation - it will indicate that the field is set for auto-increment.

Compile the sources using the already familiar glagol compile command.

Now we told Glagol DSL about all the metadata required to bundle the entity with an existing database table.

Songs repository

Lets insert some data into the database using the Song entity!

First, we need a Repository object. In general, repositories are a special type of objects that hold the sole responsibility of supporting the lifecycle of entities. Moreover, a Repository is a frequently used enterprise design pattern which has been repeatedly publicised by a number of influential authors such as Martin Fowler and Eric Evans throughout the last two decades. To put it simply, a repository can persist(create, update), remove and retrieve entities from the data storage.

Glagol DSL provides a built-in syntax declaration for repositories:

namespace MusicLib

repository for Song {

}

Save this code as src/MusicLib/SongRepository.g.

Lets investigate the code snippet. From repository for Song we can conclude that repositories are, in a way, attached to entities. Additionally, Glagol DSL allows only one repository per entity (one-on-one relationship) to be created - doing otherwise will result in typecheck errors during compilation. Last but not least, the source file name has to follow the mandatory naming convention of <Entity>Repository.g where <Entity> is the name of the targeted entity.

So far so good. However, our new repository does not do anything yet. Lets change that by introducing a save method:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }
}

Yep - it is that simple. Repositories expose the persist and flush statements. The first one tells Glagol DSL that we want to initiate the persistence of the object that is being passed. Secondly, flush statement will save all the data into the data storage. Think of it this way - persist will tell Glagol DSL “Hey, here is an object for you to keep an eye on!”, and flush says “Bring my changes to the database, please!”.

Note

Entity persisting and flushing is inspired and based on Doctrine 2 ORM’s way of operating with entities.

Finally, lets save our Song entity into the database! Modify the SongController.g to use the repository:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get repository<Song>;

    index {
        Song balkanSong = new Song("Virus", "Marko Markovic Brass Band");

        songs.save(balkanSong);

        return balkanSong;
    }
}

Lets investigate this piece of code. The very first thing to notice is repository<Song> songs = get repository<Song>; - in this line we simply define a new property songs which is of type Song repository type. Moreover, you lets focus on two main keypoints here:

  • repository<Song> - this is the syntax used to address repository types;
  • get repository<Song> - this indicates that the property should be automatically set with the repository instance for a value.

Hint

The line repository<Song> songs = get repository<Song>; can be simplified to repository<Song> songs = get selfie;. In general, the selfie keyword will reflect the type declared in the property (in this case repository<Song>).

Now just compile once again and test using curl localhost:8081/song! You will see a response just similar to:

{
    "id": 1,
    "title": "Virus",
    "genre": "Balkan",
    "author": "Marko Markovic Brass Band"
}

Notice that this time there is a value assigned to the id field. Generally speaking, this is the first indicator that the data was successfully inserted into the data storage. In fact, lets query MySQL directly to see the stored data:

mysql> SELECT * FROM glagol.songs;
+----+-------+--------+---------------------------+
| id | title | genre  | author                    |
+----+-------+--------+---------------------------+
|  1 | Virus | Balkan | Marko Markovic Brass Band |
+----+-------+--------+---------------------------+
1 row in set (0.00 sec)

Removing entities

Very similarly to persisting entities we can also remove such. However, instead of the persist statement Glagol DSL provides the remove counterpart:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }

    public void remove(Song song) {
        remove song;
        flush;
    }
}

Notice, that flushing the requested changes is still necessary if you want to delete the entity records.

In the next chapter we are going to look into how to query entities using Glagol DSL’s embedded query language!