Question

Originally I was going to ask why I was having problems calling (seq) on a result set as a test for emptiness, but a bit of research showed that it's apparently because the jdbc cursor hasn't moved anywhere. Fine and dandy. Is there a way to call next() on the resultset if I don't know its name? I can bind it to a symbol in clojure, but I haven't been able to figure out how to call the method from there.

edit: in case it's not clear, I'm referring to the java resultSet method next(), not the clojure (next)

edit#2 here's a code snippet:

(defn user-exists? [email]
  (with-connection db
   (with-query-results res ["SELECT guid from users WHERE email=?" email]
    (.next res)
    (seq res))))

(Thanks for the help on .next, btw...haven't done much java interop yet)

Still, though, using (seq) throws a NullPointerException if the query didn't return anything. I'm wondering if there's a cleaner, idiomatic way to do this?

Was it helpful?

Solution

res will be bound to a Clojure sequence based on the ResultSet where each item is a map of a result record with output column names as keywords in keys. You can't get to the underlying actual ResultSet object.

Typically you don't call next at all. To be idiomatic Clojure, you would utilize the vast library of functions that operate on sequences (map, filter, reduce, etc) to produce your output. Here you might say something like:

(with-query-results res ["SELECT guid from users WHERE email=?" email]
  (map :guid res))

which would apply :guid as a function over the res seqeuence and retrieve the column value of guid in each record, returning you a new sequence of all the guids. You could then wrap that map in a filter or whatever else you needed.

In this case, seq should be what you want but I think the abstraction is leaking a bit. When I tried it I got java.sql.SQLRecoverableException: Closed Resultset: next which implies to me the ResultSet is being closed too early in the abstraction. However, I found empty? worked ok for this use case.

(defn user-exists? [email]
  (with-connection db
   (with-query-results res ["SELECT guid from users WHERE email=?" email]
    (.next res)
    (not (empty? res)))))

This seems like a bug in clojure.core/resultset-seq to me and I reported it here as CLJ-676.

OTHER TIPS

How about

(defn user-exists? [email]
  (with-connection db
    (with-query-results res ["SELECT guid from users WHERE email=?" email]
      (first res))))

That should return something if a row is returned, or nil if there's no matching row.

Your earlier query was failing because res is the value of a call to (resultset-seq x), so you can't use Java's ResultSet.next() on it, nor do you want to call seq on it again.

As noted, you have to evaluate the resultset-seq while the ResultSet (and Connection) is open, so you would have to have

(defn users-with-email [email]
  (with-connection db
    (with-query-results res ["SELECT guid from users WHERE email=?" email]
      (doall res))))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top