Error:TupleBackedMap can not be modified. No converter found capable of converting from type org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.booksStore.entity.ReportDetailImpl]

The error occurs when you are working with a JPA native query that returns results as a TupleBackedMap (used by Spring Data for unstructured or tuple-based query results). When you try to map or cast these results to a custom interface or class, Spring can’t automatically convert them unless explicitly instructed.

RootCause:

  1. TupleBackedMap: This is an immutable result representation returned by Spring Data JPA for native queries when projecting into an interface. It’s not a standard object or mutable DTO.
  2. Conversion Error: When trying to map a query result to a custom type like ReportDetailImpl or ReportDetailsStatus, Spring needs converters, which are missing. Let’s look at a example where a native SQL query is used to fetch data from a database, and we face the issue of immutability (TupleBackedMap). I’ll walk you through the problem, the solution, and the reasoning behind it.Suppose we have two tables: Employee and Department.
public class Employee {
int id (Primary Key);
String name;
int department_id
}
public class Department{
int id (Primary key);
String name;
}

You want to fetch employee details along with their department name using a native query.

Repsository Code: 

@Query(value = "SELECT e.id AS employeeId, e.name AS employeeName, d.name AS departmentName " +
               "FROM employee e " +
               "JOIN department d ON e.department_id = d.id " +
               "WHERE d.name = :departmentName", nativeQuery = true)
List<EmployeeDetails> getEmployeeDetailsByDepartment(@Param("departmentName") String departmentName);

Interface for Projection:

public interface EmployeeDetails {
    String getEmployeeId();
    String getEmployeeName();
    String getDepartmentName();
}

Service Code:

public List<EmployeeDetails> fetchEmployeeDetails(String departmentName) {
    List<EmployeeDetails> employeeDetails = repository.getEmployeeDetailsByDepartment(departmentName);
    employeeDetails.forEach(detail -> {
        if ("HR".equals(detail.getDepartmentName())) {
            detail.setDepartmentName("Human Resources"); // ERROR: Immutable TupleBackedMap
        }
    });
    return employeeDetails;
}
Issues:
  1. Immutability:
    The results returned by the query are backed by a TupleBackedMap, which cannot be modified.
    Attempting to call a setter or update the values directly will throw an error.
  2. No Conversion:
    Spring Data JPA cannot automatically convert TupleBackedMap into mutable objects unless explicitly configured.

Solution:

Use DTO: Create a concrete class (EmployeeDetailsDTO) to hold the query results.

EmployeeDetailsDto: 

@Setter
@Getter
@AllConstructorArgs
public class EmployeeDetailsDTO {
    private String employeeId;
    private String employeeName;
    private String departmentName;}
    
Update RepositoryCode: 
@Query(value = "SELECT e.id AS employeeId, e.name AS employeeName, d.name AS departmentName " +
               "FROM employee e " +
               "JOIN department d ON e.department_id = d.id " +
               "WHERE d.name = :departmentName", nativeQuery = true)
List<EmployeeDetailsDTO> getEmployeeDetailsByDepartment(@Param("departmentName") String departmentName);

Service Code:

public List<EmployeeDetailsDTO> fetchEmployeeDetails(String departmentName) {
    List<EmployeeDetailsDTO> employeeDetails = repository.getEmployeeDetailsByDepartment(departmentName);
    employeeDetails.forEach(detail -> {
        if ("HR".equals(detail.getDepartmentName())) {
            detail.setDepartmentName("Human Resources");
        }
    });
    return employeeDetails;
}

This approach ensures the results are mutable and allows modification in the service layer.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *