Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

@Query retrieves data from a database through a Spring Data JPA repository method; it does not read records directly from a CSV, JSON, or text file. If by “file” you mean the Java file containing a repository, you can declare the query there. If your data is actually in a file, use Spring’s resource APIs and a format-aware parser—or import the data into a database if you need repeated, complex queries.

What the @Query annotation does

@Query attaches a manually written query to a Spring Data repository method. In a Spring Data JPA application, that query runs against the configured relational database. By default, the query uses JPQL, which refers to JPA entities and their properties. Set nativeQuery = true when the query is SQL written for the database’s tables and columns. The annotation does not open or parse a filesystem data file.

For example, this query can live in UserRepository.java. The query is in a Java source file, but its records come from the database:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
public interface UserRepository extends JpaRepository<User, Long> {

    @Query("select u from User u where u.email = :email")
    Optional<User> findByEmail(@Param("email") String email);
}

Spring Data JPA supports both derived query methods and explicitly declared queries. A declared @Query takes precedence over a matching named query. See the Spring Data JPA query methods reference.

Retrieve database records with JPQL

A database-backed example needs Spring Data JPA, a JDBC driver, a configured datasource, a JPA entity with an identifier, and a repository. With Spring Boot, include spring-boot-starter-data-jpa and the driver for your database; let the selected Spring Boot release manage dependency versions.

This entity uses Jakarta Persistence imports, as in Spring Boot 3 and 4 projects:

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;
    private boolean active;

    protected User() {}

    // Add constructors, getters, and setters as needed.
}

Declare queries on a repository interface. JPQL uses the entity name and Java property names—not necessarily the database table and column names:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {

    @Query("""
           select u
           from User u
           where u.active = true
           order by u.name
           """)
    List<User> findActiveUsers();

    @Query("""
           select u
           from User u
           where lower(u.name) like lower(concat('%', :term, '%'))
           """)
    List<User> searchByName(@Param("term") String term);
}

Here, User is the entity and active and name are entity properties. A query that instead says select * from users is SQL, not JPQL; mark a SQL query as native or use the version-appropriate native-query feature.

Call the repository from the application layer. A typical request flow is HTTP request → controller → service → repository → Spring Data JPA → database → mapped result. For example:

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> getActiveUsers() {
        return userRepository.findActiveUsers();
    }
}

@RestController
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users/active")
    public List<User> getActiveUsers() {
        return userService.getActiveUsers();
    }
}

The controller and service are optional architectural layers, but separating HTTP handling from persistence makes the query easier to reuse and test.

Bind parameters and choose a result type

Prefer named parameters for queries with arguments. They make the relationship between the query and method signature clear:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
@Query("select u from User u where u.email = :email and u.active = :active")
List<User> findByEmailAndActive(
        @Param("email") String email,
        @Param("active") boolean active);

Positional parameters such as ?1 and ?2 are also supported, but can be harder to maintain when a query or method signature changes. Bind user input as parameters; do not concatenate it into query text.

Choose a return type that matches the expected number and shape of results:

  • Optional<User> for a query expected to match zero or one row.
  • List<User> for multiple results.
  • Page<User> or Slice<User> for paged results.
  • A scalar or count type for a selected value or count.
  • A DTO projection when the caller needs only a subset of an entity.

A single-result contract does not make a query unique: if the query returns multiple rows, execution can fail. Use a unique predicate, a collection result, or another suitable contract. For a DTO, JPQL can select the required fields directly:

public record UserSummary(Long id, String name) {}

@Query("""
       select new com.example.demo.UserSummary(u.id, u.name)
       from User u
       where u.active = true
       """)
List<UserSummary> findActiveUserSummaries();

Replace the DTO’s package name with the one in your project. DTO projections avoid loading entity state the caller does not need. Native-query projections need compatible result mappings and, for interface projections, correctly named column aliases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

JPQL or native SQL?

Use JPQL when the query can be expressed through entities and relationships and you want less coupling to one database’s schema or SQL dialect:

@Query("select u from User u where u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

Use native SQL when you need database-specific syntax, a particular function, or exact control over SQL. Table and column names must match the actual database mapping:

@Query(
    value = "select * from users where email_address = :email",
    nativeQuery = true
)
Optional<User> findByEmailNative(@Param("email") String email);

Native SQL is less portable and more tightly coupled to table names, column names, and database behavior; its results may also need explicit mapping. Current Spring Data JPA documentation describes @NativeQuery as a composed native-query option, but whether it is available and preferable depends on the Spring Data JPA version. Check the documentation for the version used by your project.

Pagination and searching

For database-backed results that may be large, accept a Pageable argument rather than fetching every matching row:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
@Query("""
       select u from User u
       where u.active = :active
       order by u.name
       """)
Page<User> findByActive(
        @Param("active") boolean active,
        Pageable pageable);
Pageable pageable = PageRequest.of(0, 20, Sort.by("name").ascending());
Page<User> page = userRepository.findByActive(true, pageable);

Spring Data can derive a count query for many pageable queries. For complex native SQL, supply a count query explicitly when necessary:

@Query(
    value = "select * from users where active = :active",
    countQuery = "select count(*) from users where active = :active",
    nativeQuery = true
)
Page<User> findActiveUsersNative(
        @Param("active") boolean active,
        Pageable pageable);

A contains search such as LIKE '%term%' may be expensive on a large table without suitable indexing. Case sensitivity depends on the database and its collation. Consider wildcard escaping for user input; for large text-search workloads, database full-text search may be a better fit.

If the data is actually in a CSV, JSON, or text file

Use Spring’s Resource abstraction or standard Java I/O to open the file, then parse it using an appropriate parser. For a JSON file at src/main/resources/users.json, for example:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.List;

@Component
public class UserJsonReader {
    private final ObjectMapper objectMapper;
    private final Resource resource;

    public UserJsonReader(
            ObjectMapper objectMapper,
            @Value("classpath:users.json") Resource resource) {
        this.objectMapper = objectMapper;
        this.resource = resource;
    }

    public List<UserRecord> readUsers() throws IOException {
        try (InputStream input = resource.getInputStream()) {
            return objectMapper.readValue(
                    input, new TypeReference<List<UserRecord>>() {});
        }
    }
}

public record UserRecord(Long id, String name, String email) {}

Import java.io.IOException in the class containing readUsers. Use Jackson for JSON, a CSV parser for CSV, an XML parser for XML, and a line reader for plain text. For a small file, you can filter the parsed records in Java; for example, stream the list and compare each record’s email.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To configure the location rather than hard-code it, set app.users-file=classpath:data/users.csv in configuration and inject it with @Value("${app.users-file}") Resource usersFile. A filesystem resource can use a location such as file:/var/app/data/users.csv. Spring documents classpath: and file: resource locations in its resource abstraction guide; @Value can inject a resource location or property value.

Prefer Resource#getInputStream() to getFile() for classpath resources. Once the application is packaged, a resource can reside inside a JAR and may not be an independently addressable filesystem file. If the file is application.properties or YAML configuration, use Spring Boot’s externalized configuration, often with @ConfigurationProperties, rather than JPA.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When to import a file into a database

Reading and parsing a small, mostly static file can be reasonable when the application only needs occasional access. But repeated in-memory filtering rereads or retains file data and does not provide database indexes, joins, transactions, or built-in pagination. File updates and concurrent access also require their own consistency rules.

Import the file into a database when the data is large, queried frequently, shared across users or processes, or needs sorting, joins, pagination, transactional updates, or indexing. The design then becomes file → import process → database table → @Query repository method. For large imports, a dedicated batch or import job is usually more appropriate than reparsing the whole file for every request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common problems and fixes

  • Query validation fails at startup: In JPQL, check that you used the entity name and Java property names, spelled properties correctly, and matched each named parameter to an @Param. Spring Data JPA validates declared queries as part of repository setup.
  • “Table not found”: Confirm the datasource points to the expected database and that the schema exists. A typo in table mapping or an empty in-memory database can also cause this.
  • No results: Verify that the application is connected to the intended database and that matching rows and values exist. Check boolean or enum representation and database case/collation behavior. A data file is not searched by the repository query.
  • Resource not found: Check the resource path, the classpath: prefix, whether the file is packaged, or whether a filesystem path exists and is readable. Avoid assuming a classpath resource inside a JAR can be converted to a File.
  • Lazy loading fails after a repository call: Accessing a lazy relationship after the persistence context closes can cause LazyInitializationException. Consider a DTO projection, explicit fetch plan, or appropriately scoped transaction rather than making all relationships eager.
  • Too many SQL statements: Accessing relationships for each result can produce an N+1 query pattern. Consider a fetch join, entity graph, DTO projection, or a purpose-built query.
  • Native results do not map: Check selected columns, aliases, entity mappings, and database types against the result type.

Which approach should you use?

Where the records live Use
Relational database, entity-backed data Spring Data JPA repository methods; use @Query for a custom JPQL or native SQL query.
Small, static CSV, JSON, XML, or text resource Spring Resource or Java I/O plus a parser for that format.
Large, frequently queried, shared, or mutable file data Import it into a database, then query the database.
Application settings in properties or YAML Spring Boot external configuration with @Value or @ConfigurationProperties.

For simple database predicates, a derived method such as findByEmailAndActive may be clearer than @Query. For dynamic filters, consider Spring Data specifications or Querydsl; for direct SQL without JPA entity behavior, consider JdbcTemplate or another SQL-focused library. Use @Query when a database repository method is the right fit and its query is clearer written explicitly.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.