This is the second post in a short series on the topic.
The last post
looked at the tables GROUPS and TEAMS in the OpenFootball relational
database schema. There is also the table GROUPS_TEAMS, usually known as a
link table, which, ahem, "relates" the GROUPS and TEAMS table. GROUPS_TEAMS
has the following schema:
A row in GROUPS_TEAMS with group_id of XXX and team_id of YYY means that
the team represented by team_id YYY is in the group with group_id XXX.
Let's modify the Smalltalk class OFGroup to handle the linkage, by adding
the inst-var 'teams' and creating accessors for it.
Next, modify the mapping for OFGroup in OFDescriptorSystem:
It is now necessary to add the table GROUPS_TEAMS to OFDescriptorSystem:
Now let's fetch the OFGroup instances with their linked OFTeam instances.
The above snippet produces the following output:
In the snippet, logging is enabled, and the SQL generated by Glorp is
displayed in the Transcript (with whitespace inserted for readability).
What we see is the infamous "N+1 selects problem" in action - the first
SELECT fetches the GROUPS rows, then, for each group_id, there is a
corresponding SELECT to fetch the TEAMS rows.
Fortunately Glorp is cleverer than this, and provides a way to avoid the
N+1 problem, by using the message #alsoFetch:.
Same output as before, but this time the SQL (pretty-printed by hand for
readability) is much shorter and properly takes advantage of the SQL
Using OpenFootball-Glorp for
illustration, this post is the first in a series on mapping an existing
normalized database schema and other fun Glorp stuff. As usual, I'm using
SQLite for the database.
Consider the tables GROUPS and TEAMS.
As it happens, every table in OpenFootball has columns "id", "created_at"
and "updated_at", where "id" is that table's primary key. Let's take
advantage of Smalltalk's inheritance and class hierarchy to map these
columns and tables:
By convention, the Glorp mapping is encapsulated in the class OFDescriptor,
which has these supporting methods:
The mapping for OFGroup is as follows:
The mapping for OFTeam is similar and I've not shown it here for brevity.
To round out the scene setting, OFDatabase, the "database interface" class,
has class-side convenience methods to run snippets like so:
This blog is implemented in Pharo and is the natural choice for my Docker
example application. I already have a Smalltalk snippet to load this blog's
code and its dependencies into a pristine Pharo image, so I'll be using
that. Also, as a matter of course, I build the Pharo VM from source, and my
VM installation also contains self-built shared libraries like libsqlite.so
Outside of Docker, prepare a custom Pharo image:
gofaro is a simple shell script which purpose is to make sure the Pharo VM
loads my custom shared libraries, co-located with the standard VM files, at
loadSCMS1.st looks like this:
Before describing my Dockerfile, here are my conventions for inside the
VM goes into /pkg/vm.
Application artifacts including the image and changes files go into /pkg/image.
For this blog application, the blog's content is in /pkg/cms.
Starting with Ubuntu 18.04, install libfreetype6. The other lines are
copied from Torsten's tutorial.
Next, install the Pharo VM.
Now copy over the prepared Pharo image.
Finally, set the Docker container running. Here we create a UID/GID pair to
run the application. Said UID owns the mutable Pharo files in /pkg/image
and also the /pkg/image directory itself, in case the application needs to
create other files such as SQLite databases.
runSCMS1.st runs the blog application. In my current non-Dockerized
installation, the runSCMS1.st-equivalent snippet is in a workspace; for
Docker, to become DevOps/agile/CI/CD buzzwords-compliant, this snippet is
run from the command line. This is one Dockerization adaptation I had to
make to my application.
Now we build the Docker image.
The Docker image has been created, but it is not ready to run yet, because
the web content is not in the image. I'll put the content in a Docker
volume. Below, the first -v
mounts my host's content directory into /tmp/webcontent in the container;
the second -v mounts the volume smdw-content into /pkg/cms in the
container; I'm running the busybox image to get a shell prompt; and within
the container I copy the web content from the source to the destination.
Finally, run the Docker image, taking care to mount the volume
smdw-content, now with this blog's content:
Verified with a web browser. This works on my computer. :-)
MelcGraph is a graph algorithm
library by Ciprian Teodorov. I've updated the package to handle the
deprecation of #name, using 'label' in place of 'name' as inst-var and in
selectors. Thanks Ciprian for giving me commit access to the repo.