1 of 43

Intro to Databases & ORMs

CSCI 338: Software Engineering

Spring 2025

2 of 43

Announcements

  • Project 1 graded: good job!
  • We will start Lab 8 today in class and have some time to finish it on Thursday.
    • You will need to understand databases and SQLAlchemy for Project 2
  • Next week: User Interface considerations

3 of 43

Outline

  • Introduction to databases
  • Set up your database on Docker
  • Introduction to SQL
  • Introduction to Object-Relational Mappings
  • Demo

4 of 43

Outline

  • Introduction to databases
  • Set up your database on Docker
  • Introduction to SQL
  • Introduction to Object-Relational Mappings
  • Demo

5 of 43

What is a database?

A database is an organized collection of structured information, or data, typically stored electronically in a computer system.

​

How is this better than just storing data as a text file or in memory (e.g., https://meteor.unca.edu/registrar/class-schedules/api/v1/courses/2025/fall/) ?�

​

6 of 43

Why would you want to use a database?

  • ​

7 of 43

Why would you want to use a database?

  • Organizing your data: Strategies for grouping like entities together.
  • Enforcing data validation and integrity rules
  • Transaction management: if multiple people are altering the same data instance at the same time, the database will help organize these requests so that they don’t collide with each other
  • Indexing: Databases also have infrastructure for building binary search trees to speed up queries (so that searches can be done in O(logn) time).
  • SQL: Relational databases also understand SQL – a declarative language that allows a user to define the structure of a database, and to “CRUD” data.

8 of 43

Types of Databases

There are many different kinds of databases, but the two most common “families” of databases are:

  1. Relational Databases
  2. NoSQL Databases

9 of 43

Types of Databases: Relational (SQL) Databases

  • Approach: Data is organized into tables with predefined schemas, where rows represent records and columns represent fields.
  • Examples: PostgreSQL, Oracle, Microsoft’s SQL Server, MySQL, SQLite
  • Strengths:
    • Ensures data integrity with transactions
    • Structured Query Language (SQL): Industry standard for querying data.
    • Excellent at asking questions across multiple resources / collections. For instance: “which employees earned > $50K in revenue this week in under 40 hours?
  • Weaknesses:
    • Can be overkill for data that is fairly unstructured (e.g., storing chat messages, etc.)
    • Scalability: Hard to scale for very large datasets

10 of 43

Types of Databases: NoSQL Databases

  • Approach: A non-tabular database designed for flexibility, with different models (e.g., document, key-value, column-family, graph).
  • Examples: MongoDB, Firebase, CouchDB (document stores), Redis (Key-Value Stores, Neo4j (Graph database)
  • Strengths:
    • Flexibility: “Schemaless” design allows storing unstructured and semi-structured data.
    • Scalability: Designed for distributed architectures.
    • Optimized for high-volume, low-latency operations.
  • Weaknesses:
    • Consistency: Availability over consistency
    • Limited support for complex queries across documents / resources.

11 of 43

Outline

  • Introduction to databases
  • Set up your database on Docker
  • Introduction to SQL
  • Introduction to Object-Relational Mappings
  • Demo

12 of 43

Activity: Set Up Your Database!

13 of 43

What did we just set up?

����� Port 5433������Your Laptop communicates with port 5432�via DBeaver + Python client

PostgreSQL Database on Docker Container running on port 5432

14 of 43

Outline

  • Introduction to databases
  • Set up your database on Docker
  • Introduction to SQL
  • Introduction to Object-Relational Mappings
  • Demo

15 of 43

What is SQL?

SQL is a declarative programming language

Meaning, you tell SQL what data operations you want it to execute, but the underlying database system figures out how to actually go about manipulating / retrieving the data.

In declarative languages, you specify the what, not the how

What other languages have you seen (recently) that are also declarative?

    • HTML & CSS
    • What else?

SQL is not only used to manipulate data, but also to define the structure and relationships of your data.

16 of 43

PostgreSQL Reference Documentation

​

​

17 of 43

Let’s navigate to Docker…

Let’s hop onto your Docker-hosted bash terminal. Do you remember the commands?

​

ps -a # to get your process id

docker exec -it <pid> bash # to get on Docker command line

18 of 43

Now let’s access PostgreSQL

From the Docker terminal, jump onto the postgresql command line interface:

​

psql -U postgres # activate the psql CLI�

> \l # list all of the databases

> \du # list all of the database users

> \c dvdrentals # connect to the DVD database

19 of 43

psql administrative commands

\q

Exits the psql shell

\l

Lists all the available databases

\c <dbname> <username>

Connect to specific database

\c photo_app_tutorial postgres

\dt

Lists all of the tables in the database you’re connected to

\d <table_name>

Describes the structure (i.e., “schema”) of a table

\d posts

\du

List all users and their roles

space bar

If you query data in a table that has multiple pages, the space bar will show you the next set of records.

q

If you query data in a table that has multiple pages, and you want to go back to the psql prompt.

20 of 43

Let’s look at your database…

First some basics:

  1. Access your database using the the pql command line tool (demo).�
  2. Note that there are data types and constraints on all of the tables. If you insert the wrong datatype, or you forget to include a required piece of data, the database will reject your request (and this is a GOOD THING). You want data integrity.

21 of 43

Quick Crash Course on Querying

  • SELECT X from Y;
  • SELECT functions (count, sum, etc.)
  • INNER JOIN … (connect tables together)
  • WHERE
    1. Comparison operators: =, >, <, like
    2. Logical operators: and, or, in, not
  • GROUP BY
  • ORDER BY

​

Let’s practice some queries (to be continued in lab)...

22 of 43

film_id | title

---------+---------------------

1 | Academy Dinosaur

2 | Ace Goldfinger

3 | Adaptation Holes

4 | Affair Prejudice

5 | African Egg

6 | Agent Truman

7 | Airplane Sierra

8 | Airport Pollock

9 | Alabama Devil

10 | Aladdin Calendar

11 | Alamo Videotape

12 | Alaska Phantom

13 | Ali Forever

14 | Alice Fantasia

15 | Alien Center

16 | Alley Evolution

17 | Alone Trip

18 | Alter Victory

19 | Amadeus Holy

20 | Amelie Hellfighters

film_id | category_id

---------+-------------

1 | 6

2 | 11

3 | 6

4 | 11

5 | 8

6 | 9

7 | 5

8 | 11

9 | 11

10 | 15

11 | 9

12 | 12

13 | 11

14 | 4

15 | 9

16 | 9

17 | 12

18 | 2

19 | 1

19 | 12

category_id | name

-------------+-------------

1 | Action

2 | Animation

3 | Children

4 | Classics

5 | Comedy

6 | Documentary

7 | Drama

8 | Family

9 | Foreign

10 | Games

11 | Horror

12 | Music

13 | New

14 | Sci-Fi

15 | Sports

16 | Travel

film table

film_category table

category table

23 of 43

There are a few different ways to join tables together….

24 of 43

title | name

---------------------+-------------

Academy Dinosaur | Documentary

Ace Goldfinger | Horror

Adaptation Holes | Documentary

Affair Prejudice | Horror

African Egg | Family

Agent Truman | Foreign

Airplane Sierra | Comedy

Airport Pollock | Horror

Alabama Devil | Horror

Aladdin Calendar | Sports

Alamo Videotape | Foreign

Alaska Phantom | Music

Ali Forever | Horror

Alice Fantasia | Classics

Alien Center | Foreign

Alley Evolution | Foreign

Alone Trip | Music

Alter Victory | Animation

Amadeus Holy | Action

Amelie Hellfighters | Music

SELECT film.title, category.name

FROM film

JOIN film_category

ON film.film_id = film_category.film_id

JOIN category

ON film_category.category_id = category.category_id

ORDER BY film.film_id

LIMIT 20;��

Green = First join connects film to film_category (joins on film_id column)

Yellow = Second join connects film_category to category (joins on category_id column)

Solution

Option 1. JOIN Syntax

25 of 43

title | name

---------------------+-------------

Academy Dinosaur | Documentary

Ace Goldfinger | Horror

Adaptation Holes | Documentary

Affair Prejudice | Horror

African Egg | Family

Agent Truman | Foreign

Airplane Sierra | Comedy

Airport Pollock | Horror

Alabama Devil | Horror

Aladdin Calendar | Sports

Alamo Videotape | Foreign

Alaska Phantom | Music

Ali Forever | Horror

Alice Fantasia | Classics

Alien Center | Foreign

Alley Evolution | Foreign

Alone Trip | Music

Alter Victory | Animation

Amadeus Holy | Action

Amelie Hellfighters | Music

SELECT film.title, category.name

FROM film, film_category, category

WHERE

film.film_id = film_category.film_id AND

film_category.category_id = category.category_id

ORDER BY film.film_id

LIMIT 20;��

Green = Which tables to pull from

Yellow = Connects columns together the tables.

Solution

Option 2. WHERE syntax

26 of 43

title | name

---------------------+-------------

Academy Dinosaur | Documentary

Ace Goldfinger | Horror

Adaptation Holes | Documentary

Affair Prejudice | Horror

African Egg | Family

Agent Truman | Foreign

Airplane Sierra | Comedy

Airport Pollock | Horror

Alabama Devil | Horror

Aladdin Calendar | Sports

Alamo Videotape | Foreign

Alaska Phantom | Music

Ali Forever | Horror

Alice Fantasia | Classics

Alien Center | Foreign

Alley Evolution | Foreign

Alone Trip | Music

Alter Victory | Animation

Amadeus Holy | Action

Amelie Hellfighters | Music

WITH film_table AS (

SELECT f.film_id, f.title, fc.category_id

FROM film AS f � JOIN film_category AS fc

ON f.film_id = fc.film_id

)

SELECT f.film_id, f.title, c.name

FROM film_table AS f�JOIN category AS c

ON f.category_id = c.category_id

ORDER BY f.film_id;

​

Defines a temporary result set that can be referenced within another SQL statement

Solution

Option 3. Common Table Expression (CTE) syntax – Thanks Connor :)

27 of 43

SQL: INSERT

INSERT INTO table_name(column1, column2, …)

VALUES (value1, value2, …);

​

28 of 43

SQL: UPDATE

UPDATE table_name

SET column1 = value1,

column2 = value2,

column3 = value3,

...

WHERE condition;

​

29 of 43

SQL: DELETE

DELETE FROM table_name

WHERE condition;

​

30 of 43

Intro to Object Relational Mapping

31 of 43

What is SQLAlchemy?

SQLAlchemy is a python abstraction that makes communication with databases “easier.”

  • It’s database agnostic
  • You can interact with the DB using raw SQL or you can use something called Object Relational Mapping
  • You create some classes (usually called “models”) that represent each of your tables
  • You access the data in your tables using instances of the classes you create.

32 of 43

Some Terminology

  • Model
  • Session
  • AsyncIO also applies to database sessions

33 of 43

What is a Model?

  • A Model class is a Python class that represents a database table or view. Think of it as the structure of the table, and how the table relates to other tables.
  • A Model instance is a Python object that represents a single database record (or row).

This approach – known as “Object Relational Mapping” – allows a more convenient way to manipulate data via the Python language.

34 of 43

What is a database session?

  • Acts as a middleman between your Python code and the database.
  • Responsible for handling all the database interactions, like querying data, inserting new records, updating existing ones, and deleting rows.
  • Think of it as a "conversation" with the database — you open the conversation, send requests (queries), get responses, and then close the conversation when you’re done.

35 of 43

A database session handles the following

  1. Connection Management: Handles the connection to the database, so you don’t have to manually open and close connections every time.
  2. Query Execution: When you run a query, the session sends that query to the database and returns the results.�
  3. Transaction Handling: If you make multiple changes to the database, they’re only saved when you explicitly commit them.�
  4. Object Tracking: If you modify an object, the session can automatically detect the changes and apply them to the database when you commit.

36 of 43

async with AsyncSession(engine) as session:

​

​

​

  • AsyncSession is the async-friendly version of the session, which works nicely with async/await.
  • async with ensures the session is properly opened and closed, even if an error occurs.

37 of 43

ORM Activity

Walkthrough of the orm_samples.py file.

38 of 43

How do we do the same kind of join using SQLAlchemy?!

39 of 43

film_id | title

---------+---------------------

1 | Academy Dinosaur

2 | Ace Goldfinger

3 | Adaptation Holes

4 | Affair Prejudice

5 | African Egg

6 | Agent Truman

7 | Airplane Sierra

8 | Airport Pollock

9 | Alabama Devil

10 | Aladdin Calendar

11 | Alamo Videotape

12 | Alaska Phantom

13 | Ali Forever

14 | Alice Fantasia

15 | Alien Center

16 | Alley Evolution

17 | Alone Trip

18 | Alter Victory

19 | Amadeus Holy

20 | Amelie Hellfighters

film_id | category_id

---------+-------------

1 | 6

2 | 11

3 | 6

4 | 11

5 | 8

6 | 9

7 | 5

8 | 11

9 | 11

10 | 15

11 | 9

12 | 12

13 | 11

14 | 4

15 | 9

16 | 9

17 | 12

18 | 2

19 | 1

20 | 12

category_id | name

-------------+-------------

1 | Action

2 | Animation

3 | Children

4 | Classics

5 | Comedy

6 | Documentary

7 | Drama

8 | Family

9 | Foreign

10 | Games

11 | Horror

12 | Music

13 | New

14 | Sci-Fi

15 | Sports

16 | Travel

film table

film_category table

category table

40 of 43

We create models that define relationships…

class Film(Base):

__tablename__ = 'film'

film_id = Column(Integer, primary_key=True)

title = Column(String(255), nullable=False)

….

language = relationship('Language', back_populates='films')

actors = relationship('Actor', secondary='film_actor', back_populates='films')

categories = relationship('Category', secondary='film_category', back_populates='films')

inventories = relationship('Inventory', back_populates='film')

41 of 43

We create models that define relationships…

class FilmCategory(Base):

__tablename__ = 'film_category'

film_id = Column(Integer, ForeignKey('film.film_id'), primary_key=True)

category_id = Column(Integer, ForeignKey('category.category_id'), primary_key=True)

last_update = Column(TIMESTAMP, nullable=False, default=datetime.utcnow)

42 of 43

We create models that define relationships…

class Category(Base):

__tablename__ = 'category'

category_id = Column(Integer, primary_key=True)

name = Column(String(25), nullable=False)

last_update = Column(TIMESTAMP, nullable=False, default=datetime.utcnow)

films = relationship('Film', secondary='film_category', back_populates='categories')

43 of 43

…making the query easier

result = await session.execute(

select(Film.title, Category.name)

.join(Film.categories)

.order_by(Film.title)

)