It is possible to provide custom implementations of methods in a Spring Data JPA repository, which enables complete control on queries and return types. The approach is as follows:
- Define an interface with the desired method signatures.
- Implement the interface to achieve the desired behavior.
- Have the Repository extend both JpaRepositoryand the custom interface.
Here is a working example that uses JpaRepository, assuming a user_table with two columns, user_id and user_name.
UserEntity class in model package:
@Entity
@Table(name = "user_table")
public class UserEntity {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "user_id")
    private Long userId;
    @Column(name = "user_name")
    private String userName;
    protected UserEntity() {}
    public UserEntity(String userName) {
    this.userName = userName;
    // standard getters and setters
}
Define interface for the custom repository in the repository package:
public interface UserCustomRepository {
    List<String> findUserNames();
}
Provide implementation class for the custom interface in the repository package:
public class UserCustomRepositoryImpl implements UserCustomRepository {
    // Spring auto configures a DataSource and JdbcTemplate
    // based on the application.properties file. We can use
    // autowiring to get a reference to it.
    JdbcTemplate jdbcTemplate;
    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    // Now our custom implementation can use the JdbcTemplate
    // to perform JPQL queries and return basic datatypes.
    @Override
    public List<String> findUserNames() throws DataAccessException {
        String sql = "SELECT user_name FROM user_table";
        return jdbcTemplate.queryForList(sql, String.class);
    }
}
Finally, we just need to have the UserRepository extend both JpaRepository and the custom interface we just implemented.
public interface UserRepository extends JpaRepository<UserEntity, Long>, UserCustomRepository {}
Simple test class with junit 5 (assuming the database is initially empty):
@SpringBootTest
class UserRepositoryTest {
    private static final String JANE = "Jane";
    private static final String JOE = "Joe";
    @Autowired
    UserRepository repo;
    @Test
    void shouldFindUserNames() {
        UserEntity jane = new UserEntity(JANE);
        UserEntity joe = new UserEntity(JOE);
        repo.saveAndFlush(jane);
        repo.saveAndFlush(joe);
        List<UserEntity> users = repo.findAll();
        assertEquals(2, users.size());
        List<String> names = repo.findUserNames();
        assertEquals(2, names.size());
        assertTrue(names.contains(JANE));
        assertTrue(names.contains(JOE));
    }
}