Stop Writing SQL — Let Spring Data JPA Handle It

📘 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

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare