This week, I moved one step ahead from just building REST APIs to connecting them with a real database using Spring Data JPA and H2 Database.
This helped me understand how data actually flows between the backend and the database β and how Spring simplifies everything for us.
πΉ Why Spring Data JPA?
Before using JPA, I tried understanding the evolution:
- JDBC β Too much code: connection, statements, result sets, closing resources.
- Spring JDBC β Reduced boilerplate but still SQL-heavy.
- ORM (Object Relational Mapping) β Maps Java classes to database tables, making data handling object-oriented.
- JPA (Java Persistence API) β A standard for ORM so we can switch tools like Hibernate easily.
- Spring Data JPA β Builds on top of JPA and gives ready-made repository methods like save(), findAll(), deleteById() etc.
π‘ In short:
Spring Data JPA lets you work with objects instead of SQL queries, saving time and effort.
πΉ Setting Up Spring Data JPA + H2
Added dependencies in pom.xml for:
spring-boot-starter-data-jpa
h2
Configured my H2 in-memory database in application.properties:
spring.datasource.url=jdbc:h2:mem:tejasvi
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
spring.jpa.show-sql=true
π‘ Tip:
jdbc:h2:mem:tejasvi creates an in-memory DB (wiped when app stops).
To persist data even after restart, use jdbc:h2:~/tejasvi.
πΉ Building Repository Layer
Created an interface for database operations:
public interface ProductRepo extends JpaRepository {}
No need to write SQL queries anymore β this gives me all CRUD methods automatically.
π‘ Bonus:
If I want a custom finder, I can just define:
List findByName(String name);
Spring automatically generates the query!
πΉ Controller + Service Layer
Controller to handle HTTP requests:
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService service;
@GetMapping("/{id}")
public Product getProduct(@PathVariable int id) {
return service.getProductById(id);
}
@PostMapping
public void addProduct(@RequestBody Product product) {
service.addProduct(product);
}
@PutMapping
public void updateProduct(@RequestBody Product product) {
service.updateProduct(product);
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable int id) {
service.deleteProduct(id);
}
}
π‘ Used annotations:
@RestController β Combines @Controller + @ResponseBody
@RequestBody β To map JSON input from Postman to Java objects
@PathVariable β To read parameters from URL
πΉ Common Problems & How I Fixed Them
Problem 1: Table not found (or βno such tableβ)
Cause:
I forgot to annotate my model class with @Entity.
Without @Entity, JPA doesnβt recognize it as a table to create in H2.
Solution:
Add these annotations to your class:
@Entity
@Table(name = "products")
public class Product {
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private double price;
}
Also make sure spring.jpa.hibernate.ddl-auto=update is set, so Hibernate creates or updates tables automatically.
Problem 2: βFailed to convert value of type 'java.lang.String' to required type 'int'β
Cause:
Sent wrong data format in Postman. For example, ID was sent as a string ("id": "1") instead of a number.
Solution:
Always match data types correctly while sending JSON.
Example valid body:
{
"name": "Laptop",
"price": 55000
}
Problem 3: H2 console not opening
Cause:
Didnβt enable console in properties file.
Solution:
Add:
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Then open: http://localhost:8080/h2-console
and make sure JDBC URL matches whatβs in your properties file.
Problem 4: Data not visible in H2 after POST request
Cause:
Used an in-memory database (mem:). Once the app restarts, all data is gone.
Solution:
Use persistent database for local testing:
spring.datasource.url=jdbc:h2:~/tejasvi
This creates a file-based DB stored in your system.
Problem 5: βDetached entity passed to persistβ or duplicate insert errors
Cause:
When calling save() for existing entities, JPA tries to insert instead of update.
Solution:
Always include the correct ID for updates:
product.setId(existingId);
repository.save(product);
Or check first using findById() before updating.
Problem 6: @Autowired NullPointerException
Cause:
Created service/repo object manually using new keyword instead of letting Spring inject it.
Solution:
Never use new for beans. Always annotate:
@Service
public class ProductService { ... }
@Autowired
private ProductService service;
Let Spring handle object creation and dependency injection.
β Week 3 Takeaways
β Understood the evolution from JDBC β JPA β Spring Data JPA
β Configured and used H2 Database successfully
β Built CRUD APIs using JpaRepository
β Tested all endpoints using Postman
β Learned to fix real-time errors like missing @Entity, wrong JSON types, and dependency issues
β Understood how Spring auto-generates queries and handles transactions
π‘ Why Iβm Sharing
Learning backend development is not just about writing code β itβs about understanding why errors happen and how to fix them smartly.
By documenting my weekly learnings, I can reflect on my mistakes and also help other beginners avoid the same pitfalls while learning Spring Boot.
Top comments (0)