Search code examples
oracle-databasestored-proceduresclojuredatabase-cursor

how to call an Oracle procedure


How to call an Oracle stored procedure which takes a string and a date as input and returns a refcursor as output using Clojure?

variable rc refcursor;
exec SOMEDB.PKG.GETPOSITIONS('ABC', to_date('2012-02-07','yyyy-mm-dd'), :rc)
print rc

Solution

  • This is how I do it. I have included a few helper fn's that I use to convert strings to SQL dates.

    (ns foo
      (:require [clojure.java.jdbc :as jdbc]
                [clojure.string :as cs]))
    
    (def conn
      {:classname "oracle.jdbc.OracleDriver"
       :subprotocol "oracle:thin"
       :subname "@//host.name.here:port.here/db.here"
       :user "user"
       :password "pass"})
    
    (defonce ORACLE_CURSOR oracle.jdbc.driver.OracleTypes/CURSOR)
    
    (defn date-to-sql-date [date]
      (java.sql.Date. (.getTimeInMillis
                        (java.util.GregorianCalendar.
                          (+ (.getYear date) 1900) (.getMonth date) (.getDate date)))))
    
    (defn string-to-sql-date [date]
      ;; assumes American date formats
      (if-not (empty? date)
        (date-to-sql-date (java.util.Date. (cs/replace date #"-" "/")))))
    
    (jdbc/with-connection conn
      (with-open [stmt (.prepareCall (jdbc/connection) "{ call SOMEDB.PKG.GETPOSITIONS(?, ?, ?) }")]
        (doto stmt
          (.setString 1 "ABC")
          (.setDate 2 (string-to-sql-date "2012-02-07"))
          (.registerOutParameter 3 ORACLE_CURSOR)
          (.execute))
        (resultset-seq (. stmt getObject 3)))) ;; lazy-sequence
    

    resultset-seq

    NOTE: you may need to consume the entire sequence if you plan on returning it from a fn since the connection will close as soon as the fn returns.