JpaRepository findAll returns empty List

13,006

Solution 1

The problem is that your properties are not mutable. You have exposed getters and setters but you didn't specify access level, and they are not public by default, so hibernate can not see them and hence can not populate your entity with the record returned from the database. Making them public should resolve the issue.

Solution 2

Although the answer above fixes the issue, I have encountered a similar situation where repository.findById(id) would return a result and repository.findAll() would return empty.

It turns out that I had wrapped the caller method with @Transactional(readOnly = true), where the method would write and then read all records:

@Override
    @Transactional(readOnly = true)
    public List<Object> writeThenReadAll(...){
       repository.save(...);
       ...
       Object byId = repository.findById(1L).get(); //not null
       List<Object> all = repository.findAll(); //returns empty
       return all;
    }

changing @Transactional(readOnly = true) to @Transactional(readOnly = false) fixed the issue.

Share:
13,006
Filip Pranklin
Author by

Filip Pranklin

Currently I am employed as an Android Developer and I'm also a student at FER.

Updated on June 13, 2022

Comments

  • Filip Pranklin
    Filip Pranklin almost 2 years

    findAll() method from JpaRepository returns empty value, but correct number of empty values

    I'm using h2 database and everything has worked fine until some unknown moment. Simple GET at http://localhost:8080/users returns {} x number of users previously added to the database. I tried implementing a method that would return id based on username and that works just fine.

    Here is my User.java:

    @Entity
    @Table(name = "Users")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        @Column(name = "username")
        @NotBlank(message = "Username is mandatory")
        @Size(min = 1, max = 20, message = "Username must be less than 20 characters long")
        private String username;
        @Column(name = "balance")
        private Double balance = 0.0;
    
        Long getId() {
            return id;
        }
    
        void setId(Long id) {
            this.id = id;
        }
    
        String getUsername() {
            return username;
        }
    
        void setUsername(String username) {
            this.username = username;
        }
    
        Double getBalance() {
            return balance;
        }
    
        void setBalance(Double balance) {
            this.balance = balance;
        }
    }
    

    Here is UserService which implements methods from IUserService:

    @Service
    public class UserService implements IUserService {
    
        @Autowired
        private UserRepository repository;
    
        @Override
        public void createNewUser(User user) {
            repository.save(user);
        }
    
        @Override
        public List<User> findAll() {
            return repository.findAll();
        }
    
        @Override
        public Long findByUsername(String username) {
            return repository.findByUsername(username);
        }
    
        @Override
        public User findById(Long id) {
            return repository.findById(id).orElse(null);
        }
    
        @Override
        public boolean checkIfUsernameIsTaken(User user) {
            return repository.findByUsername(user.getUsername()) != null;
        }
    
        @Override
        public void deleteUser(Long id) {
            repository.deleteById(id);
        }
    
        @Override
        public void updateBalance(Long id, Double balance) {
            repository.updateBalance(id, balance);
        }
    }
    

    I tried with and without @Column annotations and it doesn't seem to do anything.

    The output I get from Postman is [{}] if I added only one user via createNewuser(), [{},{}] if I added two users and so on. I don't understand what broke the findAll() method.

    P.S. updateBalance() doesn't work either, but that's for some other time.

    Edit: some ov you asked for UserController:

    @RestController
    public class UserController {
    
        @Autowired
        IUserService userService;
    
        @GetMapping("/users")
        public List<User> findUsers() {
            return userService.findAll();
        }
    
        @GetMapping("/users/{id}")
        public User findUserById(@PathVariable Long id) {
            return userService.findById(id);
        }
    
        @PostMapping("/users")
        public ResponseEntity<Object> createUser(@RequestBody User user) {
    
            if (userService.checkIfUsernameIsTaken(user)) {
    
                Map<String, Object> response = new HashMap<>();
                response.put("status", HttpStatus.NOT_ACCEPTABLE);
                response.put("errors", "Username is already taken");
                response.put("timestamp", new Date());
    
                return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
            } else {
                userService.createNewUser(user);
                User currentUser = userService.findById(userService.findByUsername(user.getUsername()));
                Map<String, Object> response = new HashMap<>();
                response.put("id", currentUser.getId());
                response.put("username", currentUser.getUsername());
                response.put("balance", currentUser.getBalance());
                return new ResponseEntity<>(response, HttpStatus.OK);
            }
        }
    
        @DeleteMapping("/users/{id}")
        public void deleteUser(@PathVariable Long id) {
            userService.deleteUser(id);
        }
    
        @PutMapping("/users/{id}/{balance}")
        public void updateBalance(@PathVariable Long id, @PathVariable Double balance) {
            userService.updateBalance(id, balance);
        }
    }
    

    and UserRepository:

    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
    
        @Query("SELECT id FROM User WHERE username = ?1")
        Long findByUsername(String username);
    
        @Transactional
        @Modifying
        @Query("UPDATE User SET balance = ?2 WHERE id = ?1")
        void updateBalance(Long id, Double balance);
    }
    

    My problems first appeared (or so I think) after I implmented the update query, but I tried running a version where I know it worked on a different computer, but it didn't work.

  • Jebil
    Jebil almost 5 years
    Hibernate as well as HttpMessageConverters cannot see the properties that's why { } is returned, Otherwise { "id" : null , "username" : null} would have returned. So making Getters and Setters public in the entity class should solve this issue
  • Jebil
    Jebil almost 5 years
    of-course, you mentioned Hibernate , I just added the reason for empty json object too
  • Filip Pranklin
    Filip Pranklin almost 5 years
    Thank you! The IDE(IntelliJ Idea) suggested that getters and setters could be package-private so I made them package-private.
  • NiVeR
    NiVeR almost 5 years
    Often, IntelliJ, tries to be smarter than it actually is :)
  • Jay Teli
    Jay Teli about 2 years
    Thanks. In the Entity class , i changed private int id; to public int id; and now i am getting desired response.