When working with Spring Data JPA, it’s common to use custom queries to retrieve partial data from entities. But what happens when you want to return a custom object — like a quartet of values — instead of a full entity or raw Object[]? By default, JPA and Hibernate can return results as Tuple or Object[], but to convert those into a custom class such as a Quartet, some additional setup is required.
In this comprehensive guide, we’ll explore how to cast query results (especially tuples) into a custom object in a clean, reusable, and type-safe way in a Spring application.
📌 Table of Contents
- What Is a Tuple in JPA?
- Common Use Case: Return Multiple Fields
- Approach 1: Constructor Expression in JPQL
- Approach 2: Native Query with Manual Mapping
- Approach 3: Using Interface Projections
- Troubleshooting: “No Converter Found” Error
- Conclusion
What Is a Tuple in JPA?
In JPA, a Tuple represents a single row from a query result that returns multiple columns, especially when you’re using a custom SELECT that doesn’t map to an entity. It’s similar to a Map<String, Object> but allows indexed access via aliases or positions.
Example:
Tuple result = ...;
String consentId = result.get("consent_id", String.class);
But using Tuple directly is cumbersome — let’s look at more structured alternatives.
Common Use Case
Suppose you’re working with a SecuritiesAccountEntity and want to retrieve only the following fields:
consentIdibancurrencybban
You don’t need the entire entity — just these four values, and you want them grouped into a custom object like a Quartet<String, String, String, String>.
✅ Approach 1: Constructor Expression in JPQL (Recommended)
If you have a DTO class with a constructor that matches the fields, you can use JPQL’s constructor expression.
Step 1: Create the DTO or Quartet Class
public class Quartet<A, B, C, D> {
private A first;
private B second;
private C third;
private D fourth;
public Quartet(A first, B second, C third, D fourth) {
this.first = first;
this.second = second;
this.third = third;
this.fourth = fourth;
}
// Getters and toString(), equals(), hashCode() as needed
}
Step 2: Write the Query in the Repository
@Query("SELECT new com.example.dto.Quartet(e.consentId, e.iban, e.currency, e.bban) " +
"FROM SecuritiesAccountEntity e WHERE e.consentId IN (?1)")
List<Quartet<String, String, String, String>> findQuartetByConsentIds(List<String> consentIds);
📌 Note: This only works with JPQL, not native SQL.
✅ Approach 2: Native Query with Manual Mapping
For complex queries or when JPQL falls short, native queries can be used — but JPA won’t automatically map the result.
Step 1: Use Native Query in a Custom Repository
@Repository
public class CustomAccountRepositoryImpl {
@PersistenceContext
private EntityManager entityManager;
public List<Quartet<String, String, String, String>> findQuartets(List<String> consentIds) {
List<Object[]> rows = entityManager.createNativeQuery(
"SELECT consent_id, iban, currency, bban FROM securities_account_entity WHERE consent_id IN :ids"
).setParameter("ids", consentIds)
.getResultList();
return rows.stream()
.map(row -> new Quartet<>((String) row[0], (String) row[1], (String) row[2], (String) row[3]))
.collect(Collectors.toList());
}
}
This gives you full control over how the result is mapped.
✅ Approach 3: Using Interface-Based Projections
Spring Data JPA also supports interface projections, a clean and lightweight way to fetch only required fields.
Step 1: Define a Projection Interface
public interface AccountProjection {
String getConsentId();
String getIban();
String getCurrency();
String getBban();
}
Step 2: Use It in the Query
@Query("SELECT e.consentId as consentId, e.iban as iban, e.currency as currency, e.bban as bban " +
"FROM SecuritiesAccountEntity e WHERE e.consentId IN (?1)")
List<AccountProjection> findByConsentId(List<String> consentIds);
Then, convert the result to Quartet if needed:
List<Quartet<String, String, String, String>> quartets = projectionList.stream()
.map(p -> new Quartet<>(p.getConsentId(), p.getIban(), p.getCurrency(), p.getBban()))
.collect(Collectors.toList());
⚠️ Troubleshooting: “No Converter Found” Error
If you get this error:
No converter found for return value of type: Iterable<Quartet<...>>
It usually means:
- You used a native query without mapping the result.
- You returned a custom class that Spring JPA doesn’t know how to instantiate.
- You didn’t use a constructor expression or projection properly.
✅ Fix:
- Use
new com.example.YourDTO(...)in JPQL. - Use
Object[]and map manually for native queries.
🏁 Conclusion
Converting query results to a Quartet or other custom structure in Spring Data JPA is a clean and maintainable way to avoid bloated entity returns. Depending on your query type (JPQL vs native), choose:
| Scenario | Recommended Approach |
|---|---|
| JPQL + Simple DTO Mapping | Constructor Expression |
| Native SQL + Complex Mapping | Manual Mapping from Object[] |
| Clean Read-Only Views | Interface Projections + Mapping |
Using these strategies ensures your code remains modular, testable, and optimized.


