'Thymeleaf problem with Spring Data Projections

I have a query that needs to return certain fields and for that i'm using the Projections from Spring Data, but when i sent it to the front page as an attribute it didn't work because the Thymeleaf couldn't get the fields from the interface, so it returns a blank result.

I need to return the ID, name, identification and mainly the last query date (translated)

I've tried to call the field by it's full name, tryied to invoke like a method call and i also tried to use the th:each="var : ${#vars}" to see all attributes sent by the server and i got:

org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap@b0f65c2

EDIT: My Attempts:

<td th:text="${u.id}"></td>
<td th:text="${u.getId}"></td>
<td th:text="${u.getId()}"></td>
<td th:text="${u}"></td>

The front page:

<table>
  <tr>
    <th>ID</th>
    <th>Nome</th>
    <th>Identificação</th>
  </tr>
  
  <tr th:each="u : ${usuarios}")>
    <td th:text="${u.id}"></td>
    <td th:text="${u.nome}"></td>
    <td th:text="${u.identificacao}"></td>
    <td th:text="${u}"></td> <!-- TEST -->
  </tr>

The view interface:

public interface UsuarioInfoPontoView {

    Long getId();
    
    String getNome();
    
    String getIdentificacao();

    UltimaDataPontoUsuario ultimaDataPontoUsuario();
    
    interface UltimaDataPontoUsuario {
        LocalDate getData();
    }
}

Controller:

@Controller
@RequestMapping("/usuario")
public class UsuarioController {

    private UsuarioService usuarioService;
    
    @Value("${pagina.usuarios.limite_exibicao}")
    private Integer LIMITE_EXIBIR_USUARIOS_PAGINA;
    
    @Autowired
    public UsuarioController(UsuarioService usuarioService) {
        this.usuarioService = usuarioService;
    }
    
    @GetMapping("/listar")
    public String preListar(@RequestParam(defaultValue = "0", name = "page") Integer pagina, ModelMap model) {
        Pageable pageable = PageRequest.of(pagina, LIMITE_EXIBIR_USUARIOS_PAGINA);
        var t = usuarioService.findAllComPontos(pageable);
        System.out.println(t.size()); // Confirmed that is returing data.
        model.addAttribute("usuarios", t);
        
        return "usuarios/listar";
    }

The Service:

@Service
@Transactional(readOnly = true)
public class UsuarioService {

    private UsuarioRepository usuarioRepository;

    @Autowired
    public UsuarioService(UsuarioRepository usuarioRepository) {
        this.usuarioRepository = usuarioRepository;
    }
    
    public List<UsuarioInfoPontoView> findAllComPontos(Pageable pageable) {
        Page<UsuarioInfoPontoView> paginaUsuariosEncontrado = usuarioRepository.pegarUsuarioEUltimoPonto(pageable);
        return paginaUsuariosEncontrado.getContent();
    }

The Repository Interface:

@Repository
public interface UsuarioRepository extends JpaRepository<Usuario, Long>{

    Optional<Usuario> findByNome(String nome);

    Optional<Usuario> findByIdentificacao(String identificacao);

    @Query(value = "SELECT u.id, u.nome, u.identificacao, p.data "
            + "FROM Ponto p JOIN p.usuario u "
            + "ORDER BY p.id DESC", nativeQuery = false)
    Page<UsuarioInfoPontoView> pegarUsuarioEUltimoPonto(Pageable pageable);
}


Solution 1:[1]

Because it's a projection, there are no fields to speak of. Have you tried calling the interface methods like this:

<tr th:each="u : ${usuarios}">
    <td th:text="${u.getId()}"></td>
    <td th:text="${u.getNome()}"></td>
    <td th:text="${u.getIdentificacao()}"></td>
</tr>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Chaosfire