In this workshop, we'll be working with the open source database management system called Postgres. Postgres is a server that runs on a computer and listens for connections from clients such as our query tool or any programs we write that need to access data in the database.
Before you start, you need to install Docker Desktop and maven. Then, clone the git repository to a directory on your own computer. Start a terminal and navigate to this directory.
In order to start the server, we either have to install Postgres on our machine, or use a Docker image that contains Postgres. Today, we'll use a ready-made Docker image we found on the Internet that already contains a lot of data.
To start a Postgres Docker container, run the following command:
docker compose upThis command will download and start a container with our postgres database and a container with pgAdmin.
The output will be something like this:
Unable to find image 'btholt/omdb-postgres:latest' locally
latest: Pulling from btholt/omdb-postgres
dc1f00a5d701: Pull complete
3bb4b34c334c: Pull complete
4739db3ff30d: Pull complete
67627067cf92: Pull complete
8cb1fcaf0443: Pull complete
...
PostgreSQL init process complete; ready for start up.
+ echo 'PostgreSQL init process complete; ready for start up.'
+ echo
+ exec postgres
2025-03-04 16:05:35.278 UTC [1] LOG: starting PostgreSQL 14.3 (Debian 14.3-1.pgdg110+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
2025-03-04 16:05:35.278 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
2025-03-04 16:05:35.278 UTC [1] LOG: listening on IPv6 address "::", port 5432
2025-03-04 16:05:35.280 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2025-03-04 16:05:35.282 UTC [165] LOG: database system was shut down at 2025-03-04 16:05:35 UTC
2025-03-04 16:05:35.284 UTC [1] LOG: database system is ready to accept connections
----------
Loading servers with:
User: pgadmin4@pgadmin.org
SQLite pgAdmin config: /var/lib/pgadmin/pgadmin4.db
----------
Added 0 Server Group(s) and 1 Server(s).
postfix/postlog: starting the Postfix mail system
[2025-03-04 16:05:40 +0000] [1] [INFO] Starting gunicorn 22.0.0
[2025-03-04 16:05:40 +0000] [1] [INFO] Listening at: http://[::]:80 (1)
[2025-03-04 16:05:40 +0000] [1] [INFO] Using worker: gthread
[2025-03-04 16:05:40 +0000] [133] [INFO] Booting worker with pid: 133
Once you get to this point, you're ready to connect to the database.
To connect to Postgres and start running queries, we have provided a database management tool called pgAdmin. It's running on your computer at http://localhost:7777
Select save, and you should see the Nerdschool database in the left pane. Right click omdb and select Query tool.
On the right, you'll se a Query window where you'll write your SQL statements. Type in the following query and press F5 to run it.
SELECT 'Hello, Postgres!';
You should get this result:
If you want to use another query tool, for instance the one provided in IntelliJ, these are the connection properties:
- Hostname/address:
127.0.0.1 - Port:
5432 - Authentication: Username/password
- Database:
omdb - Username:
postgres - Password:
supersecret - URL: jdbc:postgresql://localhost:5432/omdb?user=postgres&password=supersecret
A database server such as Postgres contains objects such as Tables (places to store data), Views (stored, named queries) and Procedures (code that runs inside the database). These are organized in Schemas, and at the top level, Databases.
To find the tables in this database, navigate to Nerdschool > Databases > omdb > Schemas > public > Tables. You should
find a list of tables with names such as movies, casts and people. If you expand movies > Columns you'll see
which columns the movies table contains. This is useful when writing queries.
To get started, you first need to clone the git repository to your local computer. The commands below assume that you have started a terminal and navigated to the directory where the git repository is checked out.
To run the project:
mvn clean spring-boot:run After you've made changes, you must restart the project. If you use IntelliJ, simply rebuilding the project (Build -> Build Project) will trigger an automatic reload.
If you visit http://localhost:8080 you should see a list of movies released in march 1999. This list is generated
by the MovieController class, which in turn uses MovieRepository to query the database.
✏️ Change the method MovieRepository.findMovies() so that it returns all the Star Wars movies.
Hint
You can use
the LIKE keyword and the % wildcard to match parts of a string. Note that LIKE is case sensitive. There
is a Postgres specific keyword called ILIKE which does a case-insensitive comparison. This is not part of the
SQL standard, so it is not supported by all databases. The standard approach is to use
lower (m.name) LIKE %something%
In the movies table, you'll find a column called homepage, but there is no corresponding field in
the Movie java class.
✏️ Add a homepage field to the Movie java class and display a link to the homepage of the movie
in movie-list.jsp. Note: not all movies have a homepage.
We want to be able to click on a movie in the list and display more details about that movie. There should be a separate page for movie details, and that page needs its own URL. It is common practice to use the id (or other unique property) of the database row to create a unique URL. The URL for the movies page is http://localhost:8080/movies, so it would be natural to use http://localhost:8080/movies/11 for a movie with id = 11.
✏️ Create a link to the details page for each movie in movie-list.jsp. Then, in MoviesController.show(),
use the id parameter to look up the correct movie from the MovieRepository and pass it along to the
ModelAndView.
Hint:
The syntax for linking to a page on the same host is <a href="/movies/${movie.id}">${movie.name}</a>
There are lots of people involved in making a movie, and each person can be involved in several different movies,
doing different jobs. This is represented by the casts table in the database, by connecting a person, a movie and a
job.
When looking at one particular movie in the database, you can follow the relationship between the movies and the
casts table to find all the people involved in that movie. This is a One-to-Many relationship, since one movie
can have many casts, while one particular row in the casts table refers to only one movie. In JPA, this is described
with the @OneToMany annotation.
Example: A relationship between cars and their owners
@Entity
class Person {
@Id
Long id;
@OneToMany(mappedBy = "owner") //refers to the java property name "owner" in the Car class
List<Car> cars;
}
class Car {
@Id
Long id;
@ManyToOne
@JoinColumn("owner_person_id") //refers to the database column "owner_person_id" in the table "car"
Person owner;
}In this example, one Person can own many Cars, so this is a @OneToMany relationship as seen from the Person
side of the relationship. On the other hand, one Car can only have a single Person as its owner, so this is a
@ManyToOne relationship.
✏️ Use the @ManyToOne and @OneToMany annotations to connect the Movie and Cast classes, the
Person and Cast classes and the Job and Cast classes. Try out the movie detail page and you should see the
cast of the movie.
Hint:
In this database, the relationship between movie and casts is a one-to-many relationship. One movie is related to several casts. And inversely the relation between casts and movie is a many-to-one relation.
Casts also have a many-to-one relation to job and person
You should now have a property List<Cast> casts in the Movie class. One of the cast in that list has a Job
with the name "Director".
✏️ Implement the getDirector() method in Movie so that it returns the Person who was the director.
Then, display the name of that person in the movies-list page in the "Director" column.
Now that you have the director of a movie, you can create a link to a page that shows more details about that person. The URL should be http://localhost:8080/directors/1 for a person with id=1.
✏️ Add a link to the detail page of a director on the movies-list page.
You can navigate the casts relationship the other way, too. In Person there is a method called
getMoviesDirected().
✏️ Implement the Person.getMoviesDirected() method by iterating through person's casts list and returning a
list of movies where the person was the director. Then, display the list of movies on the director's detail page.
You've tried iterating through a list of cast to find directors, but in order to find all directors in this manner, you would have to iterate through all people (or all movies) and looking at all their cast. This is a task better left to the database.
In the PersonRespository, there is a method called findAllDirectors(), which has as SQL query.
✏️ Modify this query so that it joins the people, casts and jobs tables and returns only
the people who have directed at least one movie. This is a long list, so be sure to add limit 100 to
avoid fetching a lot of rows!




