📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
👋 Hey developers,
You’re working on a Java project. You need to fetch data from a database. What do you do?
If your first instinct is to write a SQL query manually, you’re doing it the hard way.
Spring Data JPA exists so that you don’t have to write SQL — at least for 90% of your queries.
In this article, we’ll explore how Spring Data JPA helps you build powerful, maintainable, and clean data access layers — with zero SQL (most of the time). And yes, we’ll cover the cases where you do need SQL as well.
🧱 What is Spring Data JPA?
Spring Data JPA is part of the Spring ecosystem that makes it easy to interact with relational databases using Java objects instead of raw SQL queries.
It:
- Works on top of JPA (Java Persistence API)
- Uses Hibernate (or other providers) under the hood
- Lets you define interfaces, and Spring auto-generates the implementations
- Automatically maps Java classes to database tables
In short: You just define your entities and repository interfaces — and Spring does the heavy lifting.
🚫 Why You Should Avoid Writing SQL in Most Cases
Writing raw SQL can:
- Be verbose and error-prone
- Make your code less readable
- Lead to tight database coupling
- Introduce SQL injection risks
- Require DB-specific tuning
Spring Data JPA lets you avoid these problems with derived query methods and a rich repository abstraction.
Let’s see how.
✅ Step-by-Step: Replacing SQL with Spring Data JPA
🧩 Step 1: Define Your Entity
Let’s say you’re building a product inventory system.
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String category;
private double price;
private boolean available;
}
This class maps to a table product
.
🔧 Step 2: Create a Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
This alone gives you:
findAll()
findById()
save()
deleteById()
count()
Zero implementation needed. No SQL written.
📌 Step 3: Write Queries Using Method Names
Here’s where the real magic begins. Spring parses your method names and generates queries automatically.
✅ Example Queries:
List<Product> findByCategory(String category);
List<Product> findByAvailableTrue();
List<Product> findByPriceLessThan(double price);
List<Product> findByNameContainingIgnoreCase(String keyword);
Under the hood, these translate to:
SELECT * FROM product WHERE category = ?;
SELECT * FROM product WHERE available = true;
SELECT * FROM product WHERE price < ?;
SELECT * FROM product WHERE LOWER(name) LIKE %?%;
You write no SQL — just descriptive method names.
🧠Optional & Robust Null Handling
Instead of returning null
, Spring supports:
Optional<Product> findById(Long id);
So you can write:
productRepository.findById(id)
.orElseThrow(() -> new ProductNotFoundException(id));
This avoids the dreaded NullPointerException
.
🚀 Real-World Use Case
Assume you’re building an e-commerce dashboard. You need to:
- Fetch all available electronics under ₹10,000
- Paginate and sort by price (descending)
With Spring Data JPA:
Page<Product> findByCategoryAndAvailableTrueAndPriceLessThan(
String category, double maxPrice, Pageable pageable);
Usage:
Pageable pageable = PageRequest.of(0, 20, Sort.by("price").descending());
Page<Product> products = productRepository
.findByCategoryAndAvailableTrueAndPriceLessThan("electronics", 10000, pageable);
In just 2 lines, you’ve:
- Filtered by 3 fields
- Sorted by price
- Paginated the result
All without SQL.
📦 Advanced Features You Still Don't Need SQL For
1. Count
long countByCategory(String category);
2. Exists
boolean existsByName(String name);
3. Projections
Return only fields you need (DTOs):
public interface ProductView {
String getName();
double getPrice();
}
List<ProductView> findByCategory(String category);
✅ Lighter queries, better performance for frontend use cases.
💬 When You Might Still Need SQL (And What to Do)
Sometimes, you can’t get away from SQL:
- Complex joins across multiple tables
- Performance-tuned native queries
- Custom aggregations
In those cases, Spring still gives you tools.
🧪 JPQL
@Query("SELECT p FROM Product p WHERE p.category = :category AND p.price < :price")
List<Product> search(@Param("category") String category, @Param("price") double price);
🧪 Native SQL
@Query(value = "SELECT * FROM product WHERE category = ?1 LIMIT 10", nativeQuery = true)
List<Product> topProductsByCategory(String category);
✅ Use sparingly, and only for performance-critical parts.
🛑 Mistakes to Avoid
Mistake | Fix |
---|---|
Overusing findAll() |
Use pagination (Pageable ) |
Returning entity from controller | Use DTOs |
Writing logic in repository | Move to service layer |
Not using Optional | Embrace Optional<T> in service layer |
Writing custom queries for simple filters | Use method names |
🧾 Summary Table
Task | Spring Data JPA Method | SQL Equivalent |
---|---|---|
Find by ID | findById(Long) |
SELECT * FROM product WHERE id = ? |
Save | save(entity) |
INSERT or UPDATE |
Delete | deleteById(id) |
DELETE |
Filter | findByName() |
WHERE name = ? |
Range filter | findByPriceBetween() |
WHERE price BETWEEN ? AND ? |
Search | findByNameContaining() |
LIKE '%text%' |
Check existence | existsByName() |
SELECT EXISTS(...) |
Count | countByCategory() |
COUNT(*) |
✅ Final Thoughts
You don’t need to be a SQL wizard to build real-world Java applications.
With Spring Data JPA, you get the power of database access — without drowning in boilerplate SQL.
So next time you're tempted to write SELECT * FROM…
, ask yourself:
- Can this be done with a method name?
- Can I use a projection or pagination?
- Do I really need raw SQL?
Start by using repository interfaces, DTOs, and method queries. Reach for @Query
only when needed.
Your future self — and your codebase — will thank you.
Comments
Post a Comment
Leave Comment