Lesson 4 of 8
The Favorites Feature — When Things Break
Estimated time: 2.5–3 hours
What You Will Learn
- Build a JPA entity to represent saved favorite jobs
- Create a Spring Data repository with a custom query method
- Build a REST controller with POST, GET, and DELETE endpoints
- Experience a real database error and understand why it happened
- Write SQL to create a table manually in MySQL Workbench
- Understand the
ddl-autooptions and when to use each one - Learn why production databases are managed differently than development ones
1. Time to Save Jobs
Your Resumator application can search for jobs. That is a great start. But think about how you actually use a job board. You search, you scroll through dozens of results, you find a few that look promising — and then what? You need to save them. You need a way to mark the jobs you care about so you can come back to them later, compare them, and eventually apply. Without a favorites feature, every time you close the browser you lose track of what you found.
In this lesson, you are going to build the ability to save jobs as favorites. You will create a new database entity, a repository, and a controller with full REST endpoints. The code you write will be clean, well-structured, and correct. You will follow every best practice we have taught you so far.
And then something is going to break.
Not because you made a mistake. Not because of a typo. Not because you forgot a semicolon. The code will be perfect. And it will still fail. That is the entire point of this lesson. What happens next — the error you encounter, the way you diagnose it, and the way you fix it — is one of the most important things you will learn in this entire course. It is the kind of experience that separates people who learned to code from people who can actually build software.
Let us get started.
2. Building the FavoriteJob Entity
The first thing you need when saving data to a database is an entity — a Java class that represents a row in a database table. In Lesson 14, you learned that the @Entity annotation tells JPA (Java Persistence API) to map your class to a table. Each field in the class becomes a column. Each instance of the class becomes a row.
For our favorites feature, we need to store everything about a job that the user wants to save. Think about what information matters: the job title, the company name, the company logo, the city and state, the job description, the link to apply, the salary range, the unique job ID from the API, and any personal notes the user might want to add. We also want to record when the job was saved.
Create a new file called FavoriteJob.java in your main package (the same package as your other classes). Here is the complete entity:
package com.example.resumator.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "favorite_jobs")
public class FavoriteJob {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "job_title", nullable = false)
private String jobTitle;
@Column(name = "employer_name", nullable = false)
private String employerName;
@Column(name = "employer_logo", length = 500)
private String employerLogo;
@Column(name = "job_city", length = 100)
private String jobCity;
@Column(name = "job_state", length = 100)
private String jobState;
@Column(name = "job_description", columnDefinition = "TEXT")
private String jobDescription;
@Column(name = "job_apply_link", length = 500)
private String jobApplyLink;
@Column(name = "job_min_salary")
private Double jobMinSalary;
@Column(name = "job_max_salary")
private Double jobMaxSalary;
@Column(columnDefinition = "TEXT")
private String notes;
@Column(name = "job_id", nullable = false)
private String jobId;
@Column(name = "saved_at", nullable = false)
private LocalDateTime savedAt;
@PrePersist
protected void onCreate() {
this.savedAt = LocalDateTime.now();
}
// Default constructor required by JPA
public FavoriteJob() {}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getJobTitle() { return jobTitle; }
public void setJobTitle(String jobTitle) { this.jobTitle = jobTitle; }
public String getEmployerName() { return employerName; }
public void setEmployerName(String employerName) { this.employerName = employerName; }
public String getEmployerLogo() { return employerLogo; }
public void setEmployerLogo(String employerLogo) { this.employerLogo = employerLogo; }
public String getJobCity() { return jobCity; }
public void setJobCity(String jobCity) { this.jobCity = jobCity; }
public String getJobState() { return jobState; }
public void setJobState(String jobState) { this.jobState = jobState; }
public String getJobDescription() { return jobDescription; }
public void setJobDescription(String jobDescription) { this.jobDescription = jobDescription; }
public String getJobApplyLink() { return jobApplyLink; }
public void setJobApplyLink(String jobApplyLink) { this.jobApplyLink = jobApplyLink; }
public Double getJobMinSalary() { return jobMinSalary; }
public void setJobMinSalary(Double jobMinSalary) { this.jobMinSalary = jobMinSalary; }
public Double getJobMaxSalary() { return jobMaxSalary; }
public void setJobMaxSalary(Double jobMaxSalary) { this.jobMaxSalary = jobMaxSalary; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
public String getJobId() { return jobId; }
public void setJobId(String jobId) { this.jobId = jobId; }
public LocalDateTime getSavedAt() { return savedAt; }
public void setSavedAt(LocalDateTime savedAt) { this.savedAt = savedAt; }
}
This is the largest entity you have written so far. Let us walk through the annotations carefully, because every single one serves a purpose:
@Entity
This tells JPA that this class represents a database table. Without this annotation, Spring would treat FavoriteJob as an ordinary Java class with no connection to the database.
@Table(name = "favorite_jobs")
By default, JPA would name the table after the class: FavoriteJob. But database naming conventions use snake_case (lowercase with underscores), not camelCase. The @Table annotation lets us explicitly set the table name to favorite_jobs, which follows proper SQL conventions.
@Id and @GeneratedValue
Every database table needs a primary key — a unique identifier for each row. @Id marks the id field as the primary key. @GeneratedValue(strategy = GenerationType.IDENTITY) tells the database to auto-increment this number. The first row gets id 1, the next gets 2, and so on. You never set this value yourself.
@Column annotations
Each @Column annotation maps a Java field to a specific database column. The name attribute sets the column name in SQL snake_case. The nullable = false attribute means the column cannot be empty — the database will reject any row that tries to leave it blank. The length attribute limits how many characters a column can hold. The columnDefinition = "TEXT" attribute tells the database to use the TEXT type instead of VARCHAR, which allows for much longer content — perfect for job descriptions.
@PrePersist
This is a lifecycle callback. The method annotated with @PrePersist runs automatically right before JPA saves the entity to the database for the first time. In our case, it sets savedAt to the current date and time. This way, we never have to remember to set the timestamp manually — JPA handles it for us every time a new favorite is saved.
Notice how the Java field names use camelCase (jobTitle, employerName, jobApplyLink) while the column names in the @Column annotations use snake_case (job_title, employer_name, job_apply_link). This is standard practice. Java conventions say fields should be camelCase. SQL conventions say columns should be snake_case. Spring and Hibernate handle the translation between the two automatically when you specify the column names explicitly. This is one of those small details that professionals handle without thinking — now you know it too.
3. Building the FavoriteJobRepository
Next, you need a repository. Remember from the earlier lessons: a repository is an interface that gives you database operations for free. You do not write the SQL yourself. You declare an interface that extends JpaRepository, and Spring generates all the implementation code behind the scenes — saving, finding, deleting, counting, and more.
Create a new file called FavoriteJobRepository.java:
package com.example.resumator.repository;
import com.example.resumator.model.FavoriteJob;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FavoriteJobRepository extends JpaRepository<FavoriteJob, Long> {
boolean existsByJobId(String jobId);
}
This is only a few lines of code, but it gives you an incredible amount of power. By extending JpaRepository<FavoriteJob, Long>, you automatically inherit all of these methods without writing a single line of implementation:
save(FavoriteJob entity)— insert or update a favorite job in the databasefindAll()— retrieve every favorite job from the databasefindById(Long id)— find a specific favorite by its iddeleteById(Long id)— delete a specific favorite by its idcount()— count how many favorites are stored
The two type parameters inside the angle brackets — FavoriteJob and Long — tell Spring which entity this repository manages and what type its primary key is. FavoriteJob is the entity class. Long is the type of the id field.
Now look at the one custom method we added: boolean existsByJobId(String jobId). This is one of the most elegant features of Spring Data JPA. You did not write any SQL. You did not write any implementation. You just declared a method name that follows a specific naming pattern, and Spring reads the method name and generates the query automatically.
existsByJobId breaks down like this: exists means "check if a record exists" (returns true or false). By means "filter by the following field." JobId refers to the jobId field in the FavoriteJob entity. So the full meaning is: "Does a row exist in the database where jobId equals this value?" Spring generates the SQL SELECT COUNT(*) > 0 FROM favorite_jobs WHERE job_id = ? behind the scenes. We will use this method to prevent duplicate favorites — if a user tries to save a job they have already saved, we can check first and show a friendly message instead of creating a duplicate.
4. Building the FavoriteController
Now you need a controller to handle HTTP requests. The controller is the layer that sits between your frontend (the browser) and your repository (the database). It receives requests, processes them, calls the repository, and sends back responses. For the favorites feature, you need three endpoints:
- POST /api/favorites — save a new favorite job
- GET /api/favorites — get the list of all saved favorites
- DELETE /api/favorites/{id} — remove a favorite by its id
Create a new file called FavoriteController.java:
package com.example.resumator.controller;
import com.example.resumator.model.FavoriteJob;
import com.example.resumator.repository.FavoriteJobRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/favorites")
@CrossOrigin(origins = "*")
public class FavoriteController {
private final FavoriteJobRepository repository;
public FavoriteController(FavoriteJobRepository repository) {
this.repository = repository;
}
@PostMapping
public ResponseEntity<?> saveFavorite(@RequestBody FavoriteJob favorite) {
// Check if this job is already saved
if (repository.existsByJobId(favorite.getJobId())) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body(Map.of("error", "This job is already in your favorites"));
}
FavoriteJob saved = repository.save(favorite);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
@GetMapping
public ResponseEntity<List<FavoriteJob>> getAllFavorites() {
List<FavoriteJob> favorites = repository.findAll();
return ResponseEntity.ok(favorites);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteFavorite(@PathVariable Long id) {
if (!repository.existsById(id)) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(Map.of("error", "Favorite not found"));
}
repository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Favorite removed successfully"));
}
}
Let us walk through this controller carefully, because it contains several important patterns:
Constructor injection: Notice the constructor that takes a FavoriteJobRepository parameter. You did not create the repository object with new. Instead, Spring sees that the controller needs a repository, creates one automatically, and passes it in. This is dependency injection — one of the core concepts of Spring. The final keyword means this field cannot be changed after the constructor runs, which is a good practice for injected dependencies.
The POST endpoint (saveFavorite): When the frontend sends a POST request with job data in the body, this method runs. First, it checks whether the job has already been saved by calling existsByJobId — the custom repository method you just created. If the job already exists, it returns a 409 Conflict status with an error message. If not, it saves the job and returns a 201 Created status with the saved entity. The @RequestBody annotation tells Spring to automatically convert the incoming JSON into a FavoriteJob object.
The GET endpoint (getAllFavorites): This is the simplest endpoint. It calls repository.findAll() to retrieve every saved favorite from the database and returns them as a JSON array with a 200 OK status.
The DELETE endpoint (deleteFavorite): The {id} in the URL path is a path variable — it gets extracted and passed as the id parameter. The method first checks if a favorite with that id exists. If not, it returns a 404 Not Found. If it does exist, it deletes it and returns a success message.
The code is clean. The code is correct. The entity is properly annotated. The repository extends the right interface. The controller handles all the edge cases. Everything follows the patterns you have learned.
Now let us run it.
5. The Moment It Breaks
Start your Spring Boot application the way you always do. Open your terminal, navigate to your project folder, and run:
./mvnw spring-boot:run
The application starts up. No errors in the console. Spring Boot prints its banner. You see the familiar "Started ResumatorApplication" message. Everything looks fine.
Open your browser. Navigate to the Resumator search page. Type in a job title — maybe "Java Developer" — and hit search. The results come back from the API, beautiful job cards fill the page. You see one that looks perfect. A Senior Java Developer position at a great company, solid salary range. You click the Favorite button.
And then you see it.
Instead of a confirmation message, your browser shows an error. You check the developer console (F12). The POST request to /api/favorites returned a 500 Internal Server Error. You switch to your terminal where Spring Boot is running, and you see a wall of red text. The error message stares back at you:
Read that error message. Read it again. Let the words sink in:
"Table 'resumator.favorite_jobs' doesn't exist."
Your code tried to save a row into a table called favorite_jobs. But that table does not exist in the database. There is no table there. Your Java code says "save this data into favorite_jobs," and MySQL responds: "I have no idea what you are talking about. That table does not exist."
Take a breath. This is not a disaster. This is a lesson.
6. Understanding What Happened
Let us think carefully about what went wrong. Your entity class says @Table(name = "favorite_jobs"). That annotation tells JPA: "When you interact with the database, use the table called favorite_jobs." But an annotation is just a label. It does not create anything. It is like writing an address on an envelope — writing the address does not build the house. The house has to already be there.
In the earlier Contact API project, you used SQLite with a property called spring.jpa.hibernate.ddl-auto=update. That setting told Hibernate: "Look at my entity classes, look at the database, and if the tables do not exist, create them automatically." It was convenient. It was magical. You wrote Java code and the tables just appeared.
But the Resumator project uses MySQL with a different configuration. Check your application.properties file. You will find something like this:
spring.datasource.url=jdbc:mysql://localhost:3306/resumator
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.jpa.hibernate.ddl-auto=none
There it is. ddl-auto=none. That single line is the reason for the error. When ddl-auto is set to none, Hibernate does absolutely nothing to the database schema. It does not create tables. It does not modify tables. It does not check whether tables exist. It assumes that every table your entities reference already exists in the database, and if they do not, it simply crashes when you try to use them.
The Root Cause
Your Java code says "save this data to the favorite_jobs table." The database says "that table does not exist." With ddl-auto=none, Spring Boot will not create the table for you. The code is correct. The database is not ready for it. The gap between the two is what caused the error.
You might be feeling frustrated right now. Why would anyone set ddl-auto=none? Why not just use update like before and let Spring handle it? That is a great question, and the answer is one of the most important things you will learn about professional software development.
7. Why We Did This on Purpose
We set ddl-auto=none deliberately. We wanted this error to happen. We wanted you to see it, feel it, and understand it. Because this is exactly what happens in real jobs, and if you only ever work with ddl-auto=update, you will be blindsided the first time you encounter a production database.
update setting is wonderful for learning and for small personal projects. But in a production environment with real customer data, letting Hibernate automatically modify your database structure is dangerous. What if it misinterprets a change and drops a column that contains important data? What if two developers make conflicting changes to their entity classes and deploy at the same time? What if a rename that seems harmless to Hibernate actually destroys a foreign key relationship? These scenarios have caused real data loss at real companies. Production databases need careful, controlled changes — not automated guesswork.
@Entity class, you should know what table it maps to. When you see a CREATE TABLE statement, you should know what the corresponding Java class would look like. This two-sided understanding is what makes you valuable. You can talk to DBAs in their language. You can debug problems that span the code-database boundary. You can read an error like "table doesn't exist" and know immediately what to do, instead of panicking or Googling for an hour.
So here is the takeaway: the error you saw was not a bug. It was a gap — a gap between what your code expects and what the database provides. In the real world, someone has to bridge that gap. Sometimes it is a DBA who runs your SQL script. Sometimes it is a migration tool that runs it automatically during deployment. Sometimes it is you, sitting at MySQL Workbench, creating the table yourself.
That is exactly what you are about to do.
8. Fixing It — Creating the Table in MySQL Workbench
Open MySQL Workbench. Connect to your local MySQL server (the connection you set up earlier in the Resumator track). Once connected, make sure you are using the resumator database. You can do this by running:
USE resumator;
Now you are going to create the table that your Java entity expects. This is the bridge between code and database. Every column in this SQL statement corresponds to a field in your FavoriteJob entity. Here is the full CREATE TABLE statement:
CREATE TABLE favorite_jobs (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
job_title VARCHAR(255) NOT NULL,
employer_name VARCHAR(255) NOT NULL,
employer_logo VARCHAR(500),
job_city VARCHAR(100),
job_state VARCHAR(100),
job_description TEXT,
job_apply_link VARCHAR(500),
job_min_salary DOUBLE,
job_max_salary DOUBLE,
notes TEXT,
job_id VARCHAR(255) NOT NULL,
saved_at DATETIME NOT NULL
);
Copy this SQL into the query editor in MySQL Workbench and click the lightning bolt icon to execute it. You should see a green checkmark and a message saying the query was successful.
Now let us walk through every single column and connect it back to the Java entity. This is where the two worlds meet:
| SQL Column | SQL Type | Java Field | Java Type | Notes |
|---|---|---|---|---|
id |
BIGINT AUTO_INCREMENT PRIMARY KEY |
id |
Long |
Auto-generated unique identifier. BIGINT maps to Java Long. AUTO_INCREMENT matches @GeneratedValue(strategy = GenerationType.IDENTITY). |
job_title |
VARCHAR(255) NOT NULL |
jobTitle |
String |
The job's title. NOT NULL matches nullable = false in the @Column annotation. Maximum 255 characters. |
employer_name |
VARCHAR(255) NOT NULL |
employerName |
String |
Company name. Also required (NOT NULL). |
employer_logo |
VARCHAR(500) |
employerLogo |
String |
URL to the company's logo image. Can be null (no NOT NULL). 500 characters for long URLs. |
job_city |
VARCHAR(100) |
jobCity |
String |
City where the job is located. Optional. |
job_state |
VARCHAR(100) |
jobState |
String |
State where the job is located. Optional. |
job_description |
TEXT |
jobDescription |
String |
Full job description. TEXT allows up to 65,535 characters — far more than VARCHAR(255). Matches columnDefinition = "TEXT". |
job_apply_link |
VARCHAR(500) |
jobApplyLink |
String |
URL to the job application page. 500 characters for long URLs. |
job_min_salary |
DOUBLE |
jobMinSalary |
Double |
Minimum salary. DOUBLE maps to Java Double. Can be null (not every job lists a salary). |
job_max_salary |
DOUBLE |
jobMaxSalary |
Double |
Maximum salary. Same type and nullable as minimum salary. |
notes |
TEXT |
notes |
String |
Personal notes the user can attach. TEXT for longer content. Optional. |
job_id |
VARCHAR(255) NOT NULL |
jobId |
String |
The unique identifier from the job search API. Required. This is what existsByJobId checks against to prevent duplicates. |
saved_at |
DATETIME NOT NULL |
savedAt |
LocalDateTime |
Timestamp of when the job was favorited. DATETIME maps to Java LocalDateTime. Set automatically by @PrePersist. |
jobTitle, employerName, jobApplyLink. Every SQL column is written in snake_case: job_title, employer_name, job_apply_link. This is not arbitrary — these are the standard conventions for each language. Java developers expect camelCase. SQL developers expect snake_case. Spring handles the mapping between the two through the @Column(name = "...") annotations you wrote in the entity. When your code says favorite.getJobTitle(), Hibernate translates that to SELECT job_title FROM favorite_jobs behind the scenes. You wrote the bridge in the annotations. Now the two sides can communicate.
After executing the CREATE TABLE statement, verify that the table was created by running:
DESCRIBE favorite_jobs;
You should see a list of all 13 columns with their types, null constraints, and key information. If you see that output, the table is ready.
9. The Moment It Works
Go back to your browser. You do not even need to restart Spring Boot — the application is still running. The code has not changed. The entity is the same. The repository is the same. The controller is the same. The only thing that changed is the database. You created the table it was looking for.
Search for a job again. Find one you like. Click the Favorite button.
This time, there is no error. No red text. No stack trace. The job is saved. You see a confirmation message. It worked.
Go back to MySQL Workbench and run this query:
SELECT * FROM favorite_jobs;
There it is. Your first favorited job, sitting in a real MySQL database table. Every field is populated. The id is 1. The saved_at timestamp shows exactly when you clicked the button. The job_title, employer_name, and all the other fields contain the real data from the job listing.
Go back to the browser. Search for more jobs. Favorite two or three more. Now run the SELECT query again in Workbench. You see multiple rows, each with its own id (2, 3, 4...), each with its own timestamp. Your data is accumulating in the database.
Now here is the real test. Go to your terminal and stop Spring Boot (press Ctrl+C). Wait a few seconds. Then start it again:
./mvnw spring-boot:run
Go back to the browser. Navigate to your favorites page (or make a GET request to /api/favorites). Every single job you saved is still there. Nothing was lost. The data survived the application restart because it lives in MySQL, not in your application's memory. This is the power of a real database.
10. Understanding ddl-auto Options
Now that you have experienced firsthand what happens with ddl-auto=none, let us look at all the available options. This property controls how Hibernate handles the gap between your Java entities and your database schema:
| Setting | What It Does | When to Use It |
|---|---|---|
none |
Does absolutely nothing. Hibernate assumes all tables already exist. If they do not, you get a runtime error when you try to use them. | Production environments where the database is managed by a DBA or migration tool. This is what we just experienced. |
validate |
Checks that the database tables match your entity classes at startup. If a table or column is missing, the application refuses to start and shows a clear error message. It never modifies the database. | Production environments where you want an early warning. Better than none because you find out about mismatches immediately instead of at runtime. |
update |
Compares your entity classes to the database and adds any missing tables or columns. It will never remove columns or tables. If you rename a field, it adds a new column but leaves the old one behind. | Development and learning. Very convenient because you do not need to write SQL. But risky in production because it can leave orphaned columns and does not handle complex changes well. |
create |
Drops all tables and recreates them every time the application starts. All existing data is destroyed. | Testing environments where you need a clean database for every test run. Never use in production — it deletes all data. |
create-drop |
Same as create, but also drops all tables when the application shuts down. The database is completely empty after the application stops. |
Automated testing only. Useful for integration tests that need a database but should leave no trace after running. |
ddl-auto=create and deploy to a production server, every record in every table will be permanently deleted the moment the application restarts. This has happened to real companies. It is one of the most catastrophic configuration mistakes you can make. Always double-check this setting before deploying.
none setting and understand why it exists, you can switch to update for the remainder of the Resumator project if you prefer. This way, if you add new entities in future lessons, Hibernate will create the tables automatically. Just remember: you are choosing convenience for learning purposes. In a real production application, you would use none or validate and manage your schema with migration scripts. To make the change, update your application.properties:
# Change from 'none' to 'update' for convenience during development
spring.jpa.hibernate.ddl-auto=update
Test Your Knowledge
1. Why did the Resumator application crash when you tried to save a favorite job?
ddl-auto=none, Hibernate does not create or modify database tables. It assumes they already exist. When the code tried to insert a row into favorite_jobs, MySQL responded that no such table existed, which caused the SQLSyntaxErrorException. The fix was to create the table manually using a CREATE TABLE statement in MySQL Workbench.2. What does the @PrePersist annotation do on the onCreate() method in the FavoriteJob entity?
@PrePersist is a JPA lifecycle callback that fires right before a new entity is inserted into the database. In this case, it automatically sets the savedAt field to the current date and time, so every favorite records exactly when it was saved without the developer needing to set it manually. This is a common pattern for timestamps like "created at" or "saved at."3. Which ddl-auto setting is safest for a production application with real customer data?
none or validate are the safest options. none does nothing to the schema, and validate only checks that tables match your entities without modifying anything. Both ensure that database changes happen through controlled, deliberate processes like migration scripts — not through automatic guesses by Hibernate. update is convenient for development but risky in production, and create or create-drop would destroy all existing customer data.Lesson Summary
This was one of the most important lessons in the entire course. You did not just learn how to build a feature — you experienced the full emotional arc of professional development: excitement, failure, understanding, and resolution. Let us recap what you accomplished:
- You built a complete JPA entity (
FavoriteJob) with 13 fields, proper column mappings, lifecycle callbacks, and getters/setters. You understand every annotation and what it does. - You created a Spring Data repository (
FavoriteJobRepository) with a custom query method that Spring generates from the method name alone. - You wrote a REST controller (
FavoriteController) with POST, GET, and DELETE endpoints, including duplicate detection, proper HTTP status codes, and error handling. - You experienced a real database error — not from a typo, not from a logic mistake, but from a gap between your code and your database. This is the kind of error that happens regularly in professional environments.
- You understood why it happened —
ddl-auto=nonemeans the database does not auto-create tables, and the table your entity referenced did not exist. - You fixed it yourself by writing a
CREATE TABLEstatement in MySQL Workbench, matching every column to its corresponding Java field. - You learned the full spectrum of
ddl-autooptions and when each is appropriate, fromnone(production) tocreate-drop(testing only).
The error was the lesson. The ability to encounter a failure, read the error message, trace it to its root cause, and fix it at the right layer — that is what separates beginners from professionals. You now have that ability. Every future database error you encounter will be less frightening because you have been through this one.
In the next lesson, you will continue building out the Resumator with additional features, building on the solid foundation you have created here. Your favorites are saved, your database is ready, and you know how to handle it when things break.
Finished this lesson?