In my last post I wrote about an error that cost me a full evening: my pom.xml had the MongoDB starter, but my code was still full of JPA annotations. The compiler kept saying cannot find symbol: class Entity.
That post was about the error. This post is about the fix — every single line I had to change to move my Task Manager project from MySQL to MongoDB.
If you are planning the same switch, this is the checklist I wish I had.
1. The dependency
Before (MySQL + JPA):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
After (MongoDB):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
One starter replaces two dependencies. And this is exactly where my problem started — I added the new one but never removed the old one, so half my code still compiled and half did not.
Remove the JPA starter completely. If you leave it in, the jakarta.persistence annotations still resolve, and you will not notice you are mixing two worlds until something breaks at runtime.
2. application.properties
Before:
spring.datasource.url=jdbc:mysql://localhost:3306/taskmanager
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
After:
spring.data.mongodb.uri=mongodb://localhost:27017/taskmanager
Five lines became one.
No ddl-auto because MongoDB has no schema to create. No dialect because there is no SQL being generated. The database and the collection are created automatically the first time you insert a document.
3. The model class
This is where most of the work was. Here is my actual Task class after the migration:
package com.taskmanager.task_manager;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "app_tasks")
public class Task {
@Id
private String id;
private String title;
private String description;
private String status = "PENDING";
@DBRef
@JsonIgnore
private User user;
public String getId() { return id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String desc) { this.description = desc; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public User getUser() { return user; }
public void setUser(User user) { this.user = user; }
}
Compare that with the JPA version it replaced:
@Entity
@Table(name = "app_tasks")
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String description;
private String status = "PENDING";
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
// getters and setters
}
The changes, one by one:
| JPA | MongoDB |
|---|---|
@Entity |
@Document(collection = "app_tasks") |
@Table(name = "...") |
folded into @Document
|
@Id from jakarta.persistence
|
@Id from org.springframework.data.annotation
|
@GeneratedValue |
not needed |
Long id |
String id |
@ManyToOne + @JoinColumn
|
@DBRef |
The import trap
@Id exists in both worlds. The annotation name is identical, so your IDE will happily auto-import the wrong one and nothing will look wrong in the editor.
If you import jakarta.persistence.Id in a MongoDB project, Spring Data does not recognise it as the document ID. Your documents get saved with a generated _id that your Java field never sees, and findById() starts returning empty for records you know exist.
Check the import line. Not the annotation.
Why the ID became a String
MongoDB generates an ObjectId — a 24-character hex value like 66b3f1c2a4e5d67890abcd12. It does not fit in a Long.
You can map it to a String and let Spring Data convert it, which is what I did. That one change then ripples through your whole project, which brings me to the part that actually broke things.
4. The repository
Before:
public interface TaskRepository extends JpaRepository<Task, Long> {
List<Task> findByStatus(String status);
}
After:
public interface TaskRepository extends MongoRepository<Task, String> {
List<Task> findByStatus(String status);
}
Two changes: the interface name and the ID type. The derived query method findByStatus works exactly the same — Spring Data reads the method name and builds a Mongo query instead of SQL.
This was the nicest surprise of the migration. If your repository only uses derived query methods, it migrates almost for free.
5. What actually broke
The model and repository were easy. These were not.
Every ID in the controller
Long id became String id in the entity, so every method signature that touched an ID had to change too:
// Before
@GetMapping("/{id}")
public Task getTask(@PathVariable Long id) { ... }
// After
@GetMapping("/{id}")
public Task getTask(@PathVariable String id) { ... }
The compiler catches most of these. What it does not catch is any place you were doing arithmetic or comparison on the ID, or parsing it with Long.parseLong(). Search your project for parseLong before you assume you are done.
@DBRef is not a join
I replaced @ManyToOne with @DBRef, and it does store a reference to the User document. But it is not a SQL join.
MongoDB fetches the referenced document with a second query. Load 50 tasks with @DBRef users and you get 51 database round trips. In SQL, one join would have done it.
For a student project with a small dataset this is fine. If your data is going to grow, the MongoDB way is to embed the fields you actually need — for example, storing just userId and username directly on the task — instead of referencing a whole document.
I kept @DBRef because my task list is per-user and never loads more than a handful at a time. Know that you are making that trade, though.
Native queries
Anything written as @Query(value = "SELECT * FROM ...", nativeQuery = true) is gone. There is no SQL to run. You rewrite those either as derived method names or as MongoDB's own query syntax:
@Query("{ 'status': ?0 }")
List<Task> findByStatusCustom(String status);
Same annotation name, completely different language inside it.
Transactions
@Transactional across multiple saves does not behave the way you expect out of the box. MongoDB supports multi-document transactions, but only on a replica set — a plain standalone local install will not give them to you.
I did not need them, so I moved on. Just do not assume the annotation is doing what it did before.
6. What I would do differently
Pick the database before writing the entity classes.
That sounds obvious. But what actually happened was: I followed a JPA tutorial, wrote my model, then read that MongoDB was "easier for flexible data" and swapped the dependency without touching anything else. The mismatch between my pom.xml and my code is what generated that cannot find symbol error in the first place.
The second thing: do not migrate a database because it sounds better. Ask what your data actually looks like. My tasks have a fixed set of fields and a clear relationship to a user — honestly, that is relational data, and MySQL was a reasonable fit for it.
I stayed on MongoDB because I wanted to learn it and because the flexible schema helps while I am still changing fields every week. That is a real reason. "It's more modern" is not.
The short checklist
- Remove the JPA starter and the SQL driver. Completely.
- Add
spring-boot-starter-data-mongodb. - Replace the datasource properties with
spring.data.mongodb.uri. -
@Entity→@Document, and check the@Idimport. -
Long id→String id, then fix every controller signature. -
JpaRepository<T, Long>→MongoRepository<T, String>. - Rewrite native SQL queries.
- Decide whether references or embedding fits your data.
If you have made this switch, I want to know one thing: did you go with @DBRef or did you embed? I am still not sure I picked right.
Top comments (0)