Search code examples
springkotlinspring-data

Unable to iterate over stream inside use{} block


I just want to print name of every user stored in database.

I am using this repository:

@Repository
interface User: JpaSpecificationExecutor<User>, PagingAndSortingRepository<User, Long> {

    @Query("from User")
    fun findAllUsers(): Stream<User>
}

inside this Service:

@Service
class MyService(val user: User) {
    private val log = LoggerFactory.getLogger(javaClass)

    @Transactional(readOnly = true)
    fun printNames() {
        log.info("here")
        user.findAllUsers().use { users ->
            users.map { it.firstName }
        }
    }
}

and it prints only here in console, but no name.

It seems like map() automatically closed the stream but I don`t know why and how to workaround it. When I put inside of use{} block only log.info(users.count()) it prints number of users stored in database. So there is a user I can print.

My question is, how can I print all names from the given stream?


Solution

  • Kotlin's use function is just a short-hand way of executing some code (your closure function) and then call close on the receiver (in this case the Stream) once done.

    What you called users is actually the Stream<User>returned by your repository, so basically your code is just calling users.map {...}. Now, the map operator is an intermediate operator, and since Java streams are lazy, they won't actually do anything until you call a terminal operator (such as .collect or .forEach).

    Assuming you want to print the user, try with:

    user.findAllUsers().use {
        it.forEach { println(it.firstName) }
    }
    

    Full working example (without Spring data):

    import java.util.stream.Stream
    
    // simulate a repository
    fun findAllUsers() = Stream.of("First", "Second", "Third")
    
    fun printNames() {
        findAllUsers().use {
            it.forEach(::println)
        }
    }
    
    fun main() {
        printNames()
    }
    

    Prints:

    First
    Second
    Third