For due date: see the team01 entry on Canvas: https://ucsb.instructure.com/courses/21167/assignments/262246
What this assignment is about: Database CRUD operations
A basic feature of many applications (not just web applications) is referred to a CRUD operations for a database table. For example, if a database tables contains students, CRUD operations would be:
- Create Student (add a new student)
- Read Student (look up a student, e.g. by their perm number, or show all students in a sorted table)
- Update Student (update some information about a student, e.g. their major, GPA, etc.)
- Destroy Student (delete a student that is no longer enrolled)
At this link, you’ll find a working app with CRUD operations for a Restaurant table:
This is the first of three team assignments (team01, team01, team03) that will add additional database tables to this app. The coding will be fairly straightforward, and will be very similar to a typical “first assignment” you might get on a real world team, in that it involves a lot of “copy and paste” coding. That is, you’ll look at an example of how to do CRUD operations for one database table, and you’ll replicate that code for another database table.
Avoid the temptation to just do this mindlessly. It is common to be assigned tasks like this in your first days in a new software development organization, because the assumption is that it gives you a chance to learn the codebase. You’ll need to navigate between two extremes, both of which raise problems:
- If you try to understand everything about every line of code, you’ll likely get bogged down in details.
- But if you just turn off your brain and code mindlessly (perhaps with the help of Github Copilot or Chatgpt), you may miss out on learning that you’ll need later when the tasks become more complex; where just copying/pasting blindly won’t get the job done.
So try to steer a middle ground between these two extremes.
team01,team02,team03: backend, frontend, integration tests
This project is divided into three phases:
- team01: You’ll focus only on the backend primarily using swagger as your tool to interact with the applications, plus writing unit tests for the backend code.
- team02: You’ll focus on the frontend code, using Storybook/Chromatic along with unit tests for React/Javascript code, as well as interactive testing of the completed app.
- team03: You’ll learn how to write integration tests using Spring Boot, and end-to-end tests using Playwright that ensure that individual parts of the app work together properly.
In this assignment, we will build the backend only for api endpoint that allow CRUD operations (Create, Read, Update, Destroy) for each of six database tables.
The type of database we’ll work with in this assignment is called an SQL database (SQL is typically pronounced like the english word “sequel”).
What you’ll do: Process
From a process standpoint, you are working with a Kanban board.
- A Kanban board is a “visualization of work in progress”
- If you’ve ever worked with a Trello board, it’s a similar idea.
- Originally, a Kanban board was a corkboard, and each item was an index card pinned to it with a thumbtack.
- These days, they are mostly online tools.
In this course, we typically work with four columns labelled:
- “todo”, “in progress”, “in review” and “done”.
There may be more columns or fewer, though typically at a minimum, there is:
- “todo”, “in progress” and “done”.
Here’s how that will play out in detail:
- Navigate to the web page for the GitHub organization, i.e. https://github.com/ucsb-cs156-f24. You’ll see a tab for
Projects
. Click on that tab. You should then see a project for your team for the team01 assignment, e.g.team01-f24-01
,team01-f24-02
,etc. - Open the link for your team’s Project. You should find four columns:
Todo
,In Progress
,In Review
,Done
- The
Todo
column will be populated with a set of tasks, which are called Issues in the GitHub implementation of Kanban. These correspond to the Issues that we’ll also see in theIssues
column of your repo. - Now navigate to your repo for , which will have a url such as: https://github.com/ucsb-cs156-f24/team01-f24-01. You will see a tab for
Issues
. Click on that tab. - You should now see a list of issues. These are the work items your team needs to complete to do the the work for the team01 assignment. They are the same issues that you find in the “To Do” column of your Kanban board (i.e. your
Project
, to use the GitHub terminology). - There may also be some additional housekeeping steps that you need to complete in order for the assignment to be considered completed; the issues on the Kanban board are not necessarily the only things you need to complete to earn full credit for the assignment. But these issues are the bulk of the work you need to divide up as a team.
- Note that you are allowed and even encouraged to add cards on the Kanban board and/or Issues for any other items you find in the assignment description that need to be completed. Tracking this on the Kanban board can be a helpful way to make sure that it get done, and to signal to other team members when it has been done.
- Each team member will take on an issue, one at a time, assign it to themselves, and move it from the “To Do” column of the Kanban board to the “In Progress” column as you start the issue. When you are finished with the issue, you move it into the “In Review” column when you’ve made a “Pull Request” to indicate that the issue is ready for your team members to review.
- Ideally, each team member should have exactly one (and only one) issue assigned to them in the In Progress column at a time.
- Once a pull request is complete for a given task, you move it into the
In Review
column- At this stage, you seek a code review from a member of the team that was not involved in the coding.
- Also, at this stage, if the PR is not “green on CI”, meaning that all of the GitHub actions scripts show green checks, this is when you should address that, before merging the pull request.
- Only when the PR is merged does the issue get moved into the
Done
column.
As long as you are not done with your contribution to the project, you should always have at least one issue in the In Progress
column (the thing you are working on to contribute to the team’s work.)
The Kanban board belongs to the team
The staff has pre-populated your Kanban board with a number of issues to help you get started. However, please be aware of these important points:
- There may be things in this assignment description, or other things that your team needs to get done that are not included on the Kanban board.
- As/when you find such things, feel free to add them to the Kanban board yourself.
The purpose of the Kanban board is primarily to serve the team as a visual representation fo the work in progress.
It is true that since this is a course, there is an aspect that you are maintaining the Kanban board for a “grade”, as part of an “assignment”—but the hope is that ultimately, you’ll see the intrinsic value of keeping a board like this up-to-date so that the team has way to see what’s going on with the project at a glance.
Repos for team01
Here are the links to the repos and Kanban (project) boards for team01
For team01, the list of issues is populated by the staff before you start using the Github Actions workflow 99-team01.yml
; your repo should have exactly issues.
If your Kanban is not yet populated, i.e. you don’t see in the todo column, click the triangle for a tutorial on how to populate the Todo column with all of the issues in your repo.
To populate the Todo
column with all issue in the repo
BEFORE YOU START: Make sure that no-one else on the team, and no-one on the staff is already doing this! If more than one team member does this, it makes a big mess. Use both your team slack channel (as well as talking to people live, in person) to coordinate this.
- Open the Kanban board (Project in Github terms)
- Click beside the
+
sign where it saysAdd issues
under the Todo column as shown in the animation below. - Type this symbol:
#
- Type the name of your repo (e.g. team01-f24-01)
- The name of your repo will pop up. Click on it.
- A dialog box will pop up. At the botton it should say (for example): Add issues from ucsb-cs156-f24/team01-f24-f24-01. Click that.
- Another pop up will appear titled
Add items to project
. There will be an checkbox at the top labelled something like25 most recent items
(the number may vary). Click this box. - Click the green button at bottom right labelled
Add selected items
- Now, if there are more items to add, a new set of items will appear. They will be added in batches of 25 until the last few are added (e.g.
17 most recent items
). Repeat steps 7 and 8 until there are no more items to add.
This animation illustrates the process:
Work on your own laptop, not CSIL
You should be working with a Java/Javascript setup on your own laptop by this point, not on CSIL.
For advice on what to install, see:
If this presents a difficulty, please contact the instructor ASAP so that some arrangement can be made for your situation.
Big Picture: what is team01 all about?
We’ll be working to create six database tables:
- Articles: for example, blog posts, newspaper articles, etc.
- UCSB Dining Commons Menu Items: food/beverage items offered by UCSB Dining Halls
- Menu Item Reviews: reviews of food/beverage items offered by UCSB Dining Halls
- Help Requests: requests for help, e.g. those on the
#help-lecture-discussion
channel of the course slack - Recommendation Requests: e.g. requests for letters of rec for grad school, scholarships, jobs
- UCSB Organizations: student orgs at UCSB
There is more information on each of these tables in the assignment.
Then, we’ll add API endpoints that allow you to create, read, update and destroy records in each of these database tables.
Click the triangle to see more detail about what that looks like on Swagger.
CRUD operations on Swagger
Here’s what the CRUD operations look like for the two example database tables in the starter code:
Once you set up your team’s team01
deployment on dokku, you should be able to try this out:
- To create a new record, use the
POST
endpoint - To see the new record you created, use either of the
GET
endpoints - To modify a record, use the
PUT
endpoint. - To delete a record, use the
DELETE
endpoint.
You are encouraged to try these out on the example database tables before starting to work on your own, so that you understand how the database is supposed to work.
The Kanban board contents
You should see issues on the board in the ToDo column when you start. From a high-level standpoint, you’ll be resolving all of the issues on the Kanban board, which are divided into two types:
- There are issues that pertain to the whole project; these are for the entire team to divide up (so about one per person, though the team can divide those up any way they see fit.)
- There are also issues for each of the database tables: for example, there are issues that pertain to the
UCSBDiningCommonsMenuItems
table. Typically, each team member will choose one database table (e.g.Articles
and then complete all of the issues pertaining toArticles
.
Set up Tasks
These appear only once on the board for the entire team; there are a total of of them.
Task |
---|
Adjusting the README.md with a list of who is working on which table |
Setting up Github Pages |
Setting up a Repo with prod deployment on Dokku |
Setting up a Repo with qa deployment on Dokku |
Adjusting the links in the README.md for the dokku deployments |
Submitting the final project on Canvas (this is the last thing the team will do) |
Coding Tasks
These are done by each team member for their database table, so each of these issues appears once for each of the database tables.
Task | Coding? |
---|---|
Setting up a personal dokku dev instance (no coding) | None: config only |
Setting up the database table (@Entity and @Repository class) (code under /src/main/java ) and setup database migration files (under src/main/resources/db/migration/changes ) | Under src/main/java and src/main/resources/db/migration/changes |
Setting up the POST operation (which creates one database row) and a GET operation to get all rows in the database | Under src/main/java and src/test/java |
Setting up an GET operation to get a single row by its id | Under src/main/java and src/test/java |
Setting up a DELETE operation (to delete a single row by its id) | Under src/main/java and src/test/java |
Setting up a PUT operation (to update a single row by its id) | Under src/main/java and src/test/java |
There is more detail both in the instructions below and on the issues themselves about how to proceed.
It’s still a team project.
Having said that, it is still the responsibility of the entire team to get all the issues for all of the tables completed. So, even if/when you are “finished with the issues for your table”, please still stay in class and help others on your team, do code reviews, and generally see where you can be helpful.
The time/effort you invest now in helping to build the capacity of your team will pay off later.
- If other members of your team are sincerely putting in effort with an intention to work for the team, but don’t have as much coding experience as you, helping those members out is both in your personal best interest, and the best interest of the team. It is something you can talk about at job interviews; for employers, this is a highly valued trait.
- On the other hand, if there are members of your team that are not really showing up, not following through, etc. this is the time to call attention to it, not in a mean or hostile way, but in a supportive, but honest way. You are encouraged to do as much as you can with friendly but candid discussions inside your team first. Messages on the team slack channel can be helpful here. If that doesn’t help, then call this to the attention of your team mentor (i.e. the TA/LA assigned to your team, see: <> for a list), and or the instructor via DMs on Slack.
- If you want to have a private 1-1 chat, that’s good too, but please start with a Slack message so that we can keep track of who is telling us what; with sixteen teams (and sometimes 32 or more teams over the course of an academic year) it gets difficult to remember who we talked to about what.
What you’ll do: Process
From a process standpoint, here’s how this project works:
- To start, each of you should clone your team’s
team01-teamname
repo, which should already have a Kanban board set up for it. - On the team’s Kanban board, there should be two types of issues:
- Ones that are global to the entire team (setup tasks)
- Ones that pertain to a particular database table
- First, divide up the set up tasks among the members of the team, and assign each of those to a team member. Leave them in the “to do” column, though, until you actually start working on the issue.
-
Then, divide up the six database tables among the team members. I suggest that you do this on your team slack channel in a single post, and then “pin” that post to your channel.
That post might look something like this:
Adam: Articles Brianna: Menu Item Reviews Chris: Help Requests Danny: Recommendation Requests Erin: UCSB Organizations Fay: UCSB Dining Commons Menu Items
At this point, whomever was assigned the issue to add the table to the README with the team assignments should be able to get that done.
-
Now look on the Kanban board. You should find that there are six issues on the Kanban board for your specific database table:
You should find all of the stories for your database item, and assign them to yourself; but drag only one into the In Progress column (and if you are already assigned to one of the set up tasks, don’t even drag that one yet)!
Typically, you should be assigned to only one item at a time in the In Progress column. The exception is if you drag an item to In Progress, make some progress on it, and then need to stop working on it for a while because you are blocked, or something else urgently needs your attention. But that should be the exception, not the normal way of doing things.
-
This YouTube video shows how to locate all of your issues and assign them to yourself. You are strongly encourage to assign all of the issues pertaining to your database table to yourself (as shown in the video) right from the start; but only drag one issues at a time into the
In Progress
column, so that the column reflects what the team is actually working on. - Now work on your issues as you did in team01; dragging them to “In Review” once they are ready for code review, and to “Done” when they are merged. Also work on the setup task to which you were assigned.
-
While the project is underway, every time class meets, you’ll start with a standup meeting.
While it is optional, many teams also find it helpful to schedule a few standups on slack/zoom or in person outside of class on days the class doesn’t meet (one or more of: Fri, Sat, Sun, Mon).
When all issues are finished, complete the “Submit on Canvas” issue. It may contain a checklist of things to review as you submit.
Getting started
To get started:
- Clone your team’s team01
- Add the https://github.com/ucsb-cs156-f24/STARTER-team01 repo as a remote called starter
git remote add starter https://github.com/ucsb-cs156-f24/STARTER-team01
This is in case there are updates to the starter code that you need to pull from by doing:
git pull starter main git push origin main
- Then you are ready to start by making your first branch, something like
Chris-RecRequestTable
git checkout -b Chris-ReqRequestTable
Also: set up your dev deployment on dokku (see the issue: “(your-database-table) - Create personal dokku dev deployment”
More details on team01
The rest of the material below is extra background/explanation to help you understand the assignment.
In this team project, our starter code has a frontend and backend, however we are still focusing only on the backend part. The frontend is a minimal frontend that provides only a place for us to login with our Google account so that we can authenticate before doing CRUD operations.
We are focusing on learning these new Spring Boot backend concepts:
- Creating SQL database tables using
@Entity
and@Repository
- Creating a
database migration
file for Liquibase migration - Using the Lombok annotations:
@Data
,@NoArgsConstructor
,@Builder
, etc. - Implementing controller routes for CRUD operations (Created, Read, Update, Destroy)
- Writing unit tests for controller CRUD operations, including the use of:
- Spring
MockMvc
- Mockito methods for creating mocks of repositories and services (
when
, `verify) - the idea of “dependency injection”
- Spring
In addition, we’ll practice further with a few concepts that we touched on in jpa03
, but may not have fully fleshed out:
- Set up of the documentation via Github Pages
- Working with feature branches, issues, a Kanban board, pull requests, and GitHub actions scripts
- Working with code coverage and mutation testing
The two database tables in the starting code
Your starter code at https://github.com/ucsb-cs156-f24/STARTER-team01 provides Spring Boot code with the ability to do CRUD operations on two database tables:
UCSBDates
UCSBDiningCommons
These tables are set up to be parallel with the data that is available through two public APIs that are provided by UCSB and documented at https://developer.ucsb.edu (though the format is slightly alterered for this assignment.)
The UCSBDates
tables has four columns, is indexed by a numeric @Id
field (private long id;
) and is intended to store data like that shown here:
[
{
"id": 1,
"quarterYYYYQ": "20234",
"name": "firstDayOfClasses",
"localDateTime": "2023-09-28T00:00:00"
},
{
"id": 2,
"quarterYYYYQ": "20234",
"name": "lastDayOfClasses",
"localDateTime": "2023-12-08T00:00:00"
}
]
The UCSBDiningCommons
table has six columns, is indexed by a string @Id
field (private String code
) and is intended to store data like that shown here:
[
{
"name": "Carrillo",
"code": "carrillo",
"hasSackMeal": false,
"hasTakeOutMeal": false,
"hasDiningCam": true,
"latitude": 34.409953,
"longitude": -119.85277
},
{
"name": "Ortega",
"code": "ortega",
"hasSackMeal": true,
"hasTakeOutMeal": true,
"hasDiningCam": true,
"latitude": 34.410987,
"longitude": -119.84709
}
]
Your task: add CRUD for additional database tables
You’ll be adding CRUD operations for six additional database tables; one per team member:
Here are the six tables you’ll be adding (one per person).
On the Kanban board, you’ll find five issues for each of these tables:
- Add database table (the
@Entity
and@Repository
classes, no test classes) - Add
GET
endpoint to list all database records, and aPOST
endpoint to create new database records, plus tests (this, and all of the rest, are done in the Controller and Controller test classes) - Add
GET
endpoint to get a single database row by its id. (plus tests) - Add
PUT
endpoint to update a single database row by its id. (plus tests) - Add
DELETE
endpoint to delete a single database row by its id. (plus tests)
You should choose one of these database tables, and then assign yourself the five issues that pertain to that database table.
As you look over these, note that some of them use an autogenerated Long
as the @Id
field, while others use a different field already in the data. That may not make any sense to you right now, but there is an explanation immediately following the list of database tables. We’ll also go over this in lecture.
(1) UCSB Dining Commons Menu Item
For details on the UCSB Dining Commons Menu database table, click the triangle
The UCSBDiningCommonsMenuItems
table will use an autogenerated Long
as its id
field, and will have these additional columns:
String diningCommonsCode
String name
String station
Here are some sample values:
id | diningCommonsCode | name | station |
---|---|---|---|
1 | ortega | Baked Pesto Pasta with Chicken | Entree Specials |
2 | ortega | Tofu Banh Mi Sandwich (v) | Entree Specials |
3 | ortega | Chicken Caesar Salad | Entrees |
5 | portola | Cream of Broccoli Soup (v) | Greens & Grains |
(2) UCSB Organization
For details on the UCSB Organization database table click the triangle
The UCSBOrganizations
table will use the orgCode
field (a String) as its @Id
field, and will have these columns:
- String orgCode
- String orgTranslationShort
- String orgTranslation
- boolean inactive
Here are some sample values:
orgCode | orgTranslationShort | orgTranslation | inactive |
---|---|---|---|
ZPR | ZETA PHI RHO | ZETA PHI RHO | false |
SKY | SKYDIVING CLUB | SKYDIVING CLUB AT UCSB | false |
OSLI | STUDENT LIFE | OFFICE OF STUDENT LIFE | false |
KRC | KOREAN RADIO CL | KOREAN RADIO CLUB | false |
(3) Recommendation Request
For details on the Recommendation request database table click the triangle
The RecommendationRequests
table will use an autogenerated Long
as its @Id
field, and will have these additional fields:
- String requesterEmail
- String professorEmail
- String explanation
- LocalDateTime dateRequested
- LocalDateTime dateNeeded
- boolean done
Here are some sample values:
id | requesterEmail | professorEmail | explanation | dateRequested | dateNeeded | done |
---|---|---|---|---|---|---|
1 | cgaucho@ucsb.edu | phtcon@ucsb.edu | BS/MS program | 2022-04-20 | 2022-05-01 | false |
2 | ldelplaya@ucsb.edu | richert@ucsb.edu | PhD CS Stanford | 2022-05-20 | 2022-11-15 | false |
3 | ldelplaya@ucsb.edu | phtcon@ucsb.edu | PhD CS Stanford | 2022-05-20 | 2022-11-15 | false |
4 | alu@ucsb.edu | phtcon@ucsb.edu | PhD CE Cal Tech | 2022-05-20 | 2022-11-15 | false |
(4) Menu Item Review
For details on the Menu Item Review database table click the triangle
The MenuItemReviews
table will use an autogenerated Long
as its @Id
field, and will have these additional fields:
- Long itemId (the id in the
UCSBDiningCommonsMenuItems
table of a menu item) - String reviewerEmail (the email of the reviewer)
- int stars (0 to 5 stars)
- LocalDateTime dateReviewed
- String comments
Pay attention to this important detail because students doing this table often get this wrong: there are two id values: id
and itemId
.
- The
id
value uniquely identifies a review. For example:- “I love the apple pie; so tasty!” might be a review with id
47
- “I hate the apple pie; tastes like cardboard” might be a review with id
53
- “I love the apple pie; so tasty!” might be a review with id
- The
itemId
is different. It identifies what item is being reviewed. It refers to the id in a different table, i.e. theUCSBDiningCommonsMenuItems
table.
For example, if that other table (UCSBDiningCommonsMenuItems
) has an entry for the Apple Pie at Ortega like this:
{
"id": 7,
"diningCommonsCode": "ortega",
"name": "Apple Pie",
"station" : "Desserts"
}
then this table, the MenuItemReviews
table might have include these two entries:
[
{
"id": 47,
"itemId": 7,
"reviewerEmail" : "cgaucho@ucsb.edu",
"stars": 5,
"comments": "I love the Apple Pie"
},
{
"id": 53,
"itemId": 7,
"reviewerEmail" : "ldelplaya@ucsb.edu",
"stars": 0,
"comments": "I hate the Apple Pie"
},
]
Here are some sample values:
id | itemId | reviewerEmail | stars | dateReviewed | comments |
---|---|---|---|---|---|
1 | 27 | cgaucho@ucsb.edu | 3 | 2022-04-20 | bland af but edible I guess |
2 | 29 | cgaucho@ucsb.edu | 5 | 2022-04-20 | best veggie pizza ever |
3 | 29 | ldelplaya@ucsb.edu | 0 | 2022-04-21 | not tryna get food poisoning, but if I were this would do it |
(5) Help Request
For details on the Help Request database table click the triangle
The HelpRequests
table will use an autogenerated Long
as its @Id
field, and will have these additional fields:
- String requesterEmail
- String teamId
- String tableOrBreakoutRoom
- LocalDateTime requestTime
- String explanation
- boolean solved
Here are some sample values:
id | requesterEmail | teamId | tableOrBreakoutRoom | requestTime | explanation | solved |
---|---|---|---|---|---|---|
1 | cgaucho@ucsb.edu | s22-5pm-3 | 7 | 2022-04-20T17:35 | Need help with Swagger-ui | false |
2 | ldelplaya@ucsb.edu | s22-6pm-3 | 11 | 2022-04-20T18:31 | Dokku problems | false |
3 | pdg@ucsb.edu | s22-6pm-4 | 13 | 2022-04-21T14:15 | Merge conflict | false |
(6) Articles
For details on the Articles database table click the triangle
The Articles
table will use an autogenerated Long
as its @Id
field, and will have these additional fields:
- String title
- String url
- String explanation
- String email (of person that submitted it)
- LocalDateTime dateAdded
Here are some sample values:
id | title | url | explanation | dateAdded | |
---|---|---|---|---|---|
1 | Using testing-playground with React Testing Library | https://dev.to/katieraby/using-testing-playground-with-react-testing-library-26j7 | Helpful when we get to front end development | phtcon@ucsb.edu | 2022-04-20 |
2 | Handy Spring Utility Classes | https://twitter.com/maciejwalkowiak/status/1511736828369719300?t=gGXpmBH4y4eY9OBSUInZEg&s=09 | A lot of really useful classes are built into Spring | phtcon@ucsb.edu | 2022-04-19 |
Adding a database table
To add an SQL database table in Spring Boot, you typically add two files:
- A Java class that is annotated with
@Entity
; each instance of this class represents a single row in the database table. Name should be a singular noun. Add the file in the same directory/package as the other@Entity
classes. - A Java class that is annotated with
@Repository
; each instance of this class represents a database table. Name should be the Entity name followed byRepository
. Add the file in the same directory/package as the other@Repository
classes.
In addition, you’ll need to add a database migration
file in order to create the table in the database. This is done by adding a file in the directory src/main/resources/db/migration/changes
that describes the changes to the database.
There is more information in the sections below as well as on the Kanban Board issues themselves to guide you through the process.
What is an @Entity
class?
Every database table starts with an `@Entity class that defines what one row of the table contains.
For the most part think of it as a “plain old java object” that just has the basic features of a class:
- private data members for each field (column)
- getters, setters, constructor,
toString
,hashCode
, andequals
(we use the@Data
annotation of Lombok to generate these automatically)
We typically use singular nouns for the entity class, e.g. UCSBDate
, UCSBDiningCommons
Two types of id values for an @Entity
class
In Spring, each @Entity
class has a primary key marked with the annotation @Id
.
This value must be unique in the database table; no two rows can have the same primary key.
There are two strategies for dealing with this requirement:
-
Autogenerated ids, which start at
1
and then increment automatically. TheUCSBDate
entity in the starter code is an example. The code looks liek this:@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id;
As an aside: you may wonder what happens when we run out of numbers. Since these
id
numbers are typically stored in a 64-bit JavaLong
, the maximum number is:9,223,372,036,854,775,807L
.- If you stored 1 Million records per second, 24 hours a day, 7 days a week, it would take you 292 thousand years to cycle through this many id numbers.
- That’s also over 18,000 records for every square meter on the face of the planet earth. Not sure what database table needs that many records.
-
Using a value already in the data that is inherently unique. For example, we might use perm number as an id field for a table of students.
The
UCSBDiningCommons
entity in the sample code shows an example, where thecode
field is guaranteed to be unique; no two dining commons will have the samecode
value:@Id private String code;
The @Entity
class in more detail
Click the triangle for more details on creating an @Entity
class
@Entity
classFor example of @Entity
classes, consult these files in the starter code:
You’ll see that these files have a particular structure, with these annotations:
UCSBDate.java:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity(name = "ucsbdates")
public class UCSBDate {
UCSBDiningCommons.java:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity(name = "ucsbdiningcommons")
public class UCSBDiningCommons {
What do these annotations do?
@Data
is an annotation from a package called Lombok. Lombok automatically generates code for some of the tedious things that can be automated: getters, setters,toString
,hashCode
andequals
. It also implements a constructor for all “required arguments”, though that one is not always very convenient to use if we have lots of fields.@AllArgsConstructor
and@NoArgsConstructor
are additional Lombok annotations that define additional constructors for us. The@NoArgsConstructor
is particularly important, since it’s a requirement of many pieces of Java Software that classes implement a no-args constructor (it’s part of what it means to be a Java Bean.)@Builder
create a class and some methods that make it easy to build objects with a syntax like this:UCSBDiningCommons commons = UCSBDiningCommons.builder() .name("Carrillo") .code("carrillo") .hasSackMeal(false) .hasTakeOutMeal(false) .hasDiningCam(true) .latitude(34.409953) .longitude(-119.85277) .build();
@Entity(name = "ucsbdiningcommons")
is the annotation that says this will be a row in a database table; the parameter sets the name of the table. We typically use all lowercase plural nouns here, with no hyphens or underscores.
With these annotations in place, it’s a simple matter of defining private fields for each of the columns in the database table.
What is a @Repository
class?
A second part of setting up a database table in Spring is creating a @Repository
class.
Note: do not confuse this use of the english word “repository” with the concept of a “repository in Git/Github. The english word “repository” means “a container in which things are stored”, and, regrettably, it was chosen, separately, by both the authors of git and the authors of Spring, to mean two very different kinds of collections.
In Spring, a @Repository
class is an abstraction for the database table itself, i.e. an instance of a @Repository
class represents the entire table of data (all of the rows and columns).
We typically name a Repository class with a name such as ___Repository
where the blank is filled in with the name of the @Entity
, e.g. instances of an @Entity
class named UCSBDate
would be stored in a UCSBDateRepository
.
It is important to understand that when you set up an @Repository
class, the types that you pass to CRUDRepository
as shown below must match the type of the @Entity
and the type of the @Id
field, as in these examples:
-
UCSBDateRepository
usesCrudRepository<UCSBDate, Long>
because the@Id
field ofUCSBDate
is aLong
:@Repository public interface UCSBDateRepository extends CrudRepository<UCSBDate, Long> { ...
-
UCSBDiningCommonsRepository
usesCrudRepository<UCSBDate, String>
because the@Id
field ofUCSBDiningCommons
is aString
:@Repository public interface UCSBDiningCommonsRepository extends CrudRepository<UCSBDiningCommons, String> { ...
As you look over your database table description above, take note of which of these applies to you. It’s important to choose the correct kind of code as your model when creating your own @Entity
, @Repository
and Controller (@RestController)
classes:
- Choose
UCSBDate
as your example to follow when the id field is going to be an integer, e.g. in the cases ofUCSBDiningCommonsMenuItems
,RecommendationRequests
,MenuItemReviews
,HelpRequests
,Articles
- Choose
UCSBDiningCommons
when the id field is going to be a unique string that’s part of the data, e.g. in the cases ofUCSBOrganizations
.
The @Repository
class in more detail
Click the triangle for more details on creating an @Repository
class
@Repository
classFor the repository class, see the examples:
Note that these are both interface
files and not classes.
Normally, if you create an interface
, you also need to create a class that implements that interface.
However, Spring Boot will automatically generate the code for you.
In addition, if you need certain kinds of queries, you can specify methods in your interface to implement those queries.
The rules for translating method naming conventions into generated code are complicated: we will not go over all of them in lecture, and you are not expected to memorize or learn them all, and you probably won’t need that for this assignment (though you may need to know it later in the course.)
In any case, if/when you do need to understand that, here is some documentation is here to help get you started: https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#jdbc.query-methods
What is a Database Migration
file?
The third part of setting up your new table is a creating a database migration file.
In Liquibase, a migration file describes how a change should be applied to a given table. It is used on live databases where the data cannot be erased between versions and need to be under continuous integration.
- Each file contains multiple
changeSets
. - Each change set has an
id
such asArticles-1
. Theid
is typically the name of the@Entity
class followed by a number; these numbers just start at 1 and increase with each change set. , -the number of thechangeSet
in the file, Example: Articles-1. - In addition to that each
changeSet
has anauthor
, somepreconditions
and most important, a list ofchanges
.
The Database Migration
file in more detail
Click the triangle for more details on creating an Database Migration
file
Database Migration
fileFor the Database Migration files, see the examples:
We describe the database changes in the changes
section of the files linked to above.
Here’s the code for one of those, namely the database migration for file UCSBDates
:
{
"databaseChangeLog": [
{
"changeSet": {
"id": "UCSBDates-1",
"author": "MattP",
"preConditions": [
{
"onFail": "MARK_RAN"
},
{
"not": [
{
"tableExists": {
"tableName": "UCSBDATES"
}
}
]
}
],
"changes": [
{
"createTable": {
"columns": [
{
"column": {
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"primaryKeyName": "CONSTRAINT_5"
},
"name": "ID",
"type": "BIGINT"
}
},
{
"column": {
"name": "LOCAL_DATE_TIME",
"type": "TIMESTAMP"
}
},
{
"column": {
"name": "NAME",
"type": "VARCHAR(255)"
}
},
{
"column": {
"name": "QUARTERYYYYQ",
"type": "VARCHAR(255)"
}
}
],
"tableName": "UCSBDATES"
}
}
]
}
}
]
}
The file is a JSON file that contains a single object with a single attribute databaseChangeLog
which is an array of changeSet
objects. These changeSet
objects describe the sets of changes
that need to be made to the database, as well as any preConditions
that decide whether or not to apply the changeSet
.
It is important that the tableName
attribute of the change
matches the @Entity(name = YOURTABLENAME)
that you provided during the creation of the @Entity
Class.
From this point forward, any time you make a change to the original entity you must add a new changeSet
to the databaseChangeLog
array with a new id
value. The changes
section of the changeSet
will describe the changes that need to be made to the database in order to match the new @Entity
class.
All changes described in these files will be applied everytime you start the app with mvn spring-boot:run
. However if the changes were already applied they will not be applied again.
For more information on Liquibase you can visit https://ucsb-cs156.github.io/topics/liquibase/
Note that these files only describe the creation of a table, however on the real world you will most likely be describing changes like adding a new column or deleting an existing table etc. for all possible changes you can make to the database using the changes sets you can see https://docs.liquibase.com/change-types/home.html
Also you might find it useful to know the commands described in the Liquibase doc. They will allow you to check and apply the changes that will happen to the database outside of just building the app
About that CONSTRAINT_5
The CONSTRAINT_5
in the example is a bit unfortunate, but we caught it too late to change it for F24; hopefully we can change this before we use the assignment again.
Here’s what that means, and what you should do in your code:
- Constraints are rules that a database table is required to follow
- When you specify that a certain field is a primary key, one of the aspects of being a primary key is that this field can have no duplicate values in the table.
- In a liquibase specification, each constraint has to be given a unique name; unique across the entire database (not just this table, but all tables in the database).
- When this example was put together, someone chose the name
CONSTRAINT_5
because, we can assume, the namesCONSTRAINT_1
,CONSTRAINT_2
, etc. were already taken. - The problem is that this isn’t a very good strategy for choosing a name; it’s difficult to know which numbers have/have not been used.
- Instead, we suggest the following strategy: Since database tables have to be unique, and a table can only have one primary key, use the name NAME_OF_TABLE_PK, for example:
- RESTAURANTS_PK
- UCSBDININGCOMMONSMENUITEMS_PK
- etc.
That is a more sustainable naming convention.
CamelCase vs. Snake Case
One unexpected thing that you will likely encounter: when a database field is named with a camelCaseTypeVariable
such as diningCommonsCode
, when you put this variable into the database migration file, you need to convert it to SNAKE_CASE
like this: DINING_COMMONS_CODE
.
If you don’t do this, you’ll likely run into errors when you try to use the POST
method and store a database record in the table; it may look something like this:
org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "UCMI1_0.DINING_COMMONS_CODE" not found;
You may be wondering why this is the case.
The root cause is related to the fact that SQL, the language used by the underlying database management system, is case insensitive, meaning that diningCommonsCode
, DININGCOMMONSCODE
and diningcommonscode
are all treated as identical in SQL.
Because of this, the designers of Hibernate, one of the layers on which Spring is built, used a strategy of mapping camel-case to snake case as described here.
It is possible to override this behavior, but this is the default.
More Hints
Here are some common problems that folks may run into.
Test for GET
by id fails, throwing EntityNotFound
exception
GET
by id fails, throwing EntityNotFound
exceptionIf you get a failure on this test:
test_that_logged_in_user_can_get_by_id_when_the_id_does_not_exist
Check that your Controller extends ApiController
- Incorrect:
public class UCSBDiningCommonsMenuItemController {
- Correct:
public class UCSBDiningCommonsMenuItemController extends ApiController {
Here’s why: The class ApiController
includes this method, which is then inherited when you extend ApiController
:
/**
* This method handles the EntityNotFoundException.
* @param e the exception
* @return a map with the type and message of the exception
*/
@ExceptionHandler({ EntityNotFoundException.class })
@ResponseStatus(HttpStatus.NOT_FOUND)
public Object handleGenericException(Throwable e) {
return Map.of(
"type", e.getClass().getSimpleName(),
"message", e.getMessage()
);
}
The code in most of our controllers relies on this exception handler to return an object of the correct type (a JSON object that contains type
and message
fields describing the exception). If this is not in place, the controller will simply throw an exception, which is not what the test code is looking for.
We may add more hints about working with the team01 code as we discover what problems studnets run into.
In the meantime, use the #help-team01
channel to ask questions.
A note about open source
Note that this assignment is open source.
The repos are public on purpose.
- You are encouraged to consult with one another within and across teams where it helps your learning.
- That does not mean that you can cheat by just copying code from another team.
- You are not permitted to just look at another team’s code, even though you “can”.
- It does mean that you should try to solve the problems as best you can, but you may consult with members of other teams as you work. In that context, you may look at other team’s code.
This isn’t hard. You all know when you are are looking at other team’s work to try to learn, versus when you are just looking at it as a shortcut to learning.
I’m trusting you to do the right thing. This is practice for when, later on, you are all working on different assignments.
Details: Controller methods and tests
The examples for the Controllers and Controller Tests are in these files:
- Controllers:
- Controller Tests:
You should be able to find the code you need for each of the methods, and use it as a model to create the code for your database table.
If you need additional guidance, ask on the #help-team01
channel, and we’ll try to steer you in the right direction.
When you are done
When all branches are merged to main, all tasks on Kanban board in the done column, please submit on Canvas.
There is no Gradescope autograder for team01; it will be graded manually.
Video Resources
Topic | Video | Length |
---|---|---|
(1) Set up dev dokku deployment | https://youtu.be/pW1LWgu3iuk | 29 minutes |
(2) Create database table | https://youtu.be/KHAKnngUyeY | 47 minutes |
(3) Create Controller, GET /all and POST | https://youtu.be/XBCJcRAsZtQ | 1 hour 3 minutes |
(4) Add GET by id to Controller | https://youtu.be/8vDoFSdblLE | 23 minutes |
(5) Add PUT (edit/update) to Controller | https://youtu.be/2Iy9TShGURk | 36 minutes |
(6) Add DELETE (edit/update) to Controller | https://youtu.be/whu7Nei6zTM | 20 minutes |
Total running time: 3 hours, 38 minutes.
Instructor Resources
Click the triangle for a list of tasks the instructor should do prior releasing this lab.
-
Create team01 repos using the https://ucsb-cs-github-linker.herokuapp.com
- Set up starter code in the course organization, and update links
- Create a Canvas assignment for team01; update the due dates and publish it
- Create projects for all of the groups. You can find a script for this here:
- https://github.com/ucsb-cs156-w24/project-creator/blob/main/scripts/team01-projects.sh You will probably need to make a new copy of that repo for this quarter in this quarter’s organization and update the scripts.
- After running the script, there are three aspects of setting up the projects that are manual:
- set view to board
- add “In Review” column
-
change team access to admin
-
Make sure the app <> is up and running, and is sync’d with the starter code:
i.e, on dokku-00 for example, do:
dokku git:sync team01 https://github.com/ucsb-cs156-f24/PRIVATE-team01 main dokku ps:rebuild team01
- Proofread the instructions in this file, and request that the staff (TAs/LAs do also)
- Consider assigning at least one TA/LA (preferably the one with the least prior experience with the course) to complete the lab in it’s entirety to debug the starter code and instructions
The next step was probably already done in team01, but just in case:
-
Be sure that the organization settings are set like this, in, for example, https://github.com/organizations/ucsb-cs156-f24/settings/actions
This is needed so that the github actions scripts have write access to the directory.
This setting is probabaly also a good idea:
For S25
- Consider updating the issues in 99-team01.yml to guide the students through branch hygeine. Otherwise, they are likely to make one big branch and one big PR for the entire project.