15

例如,这是 PostgreSQL 中的一个产品表,其状态为枚举:

create type product_status as enum ('InStock', 'OutOfStock');

create table product (
    pid            int primary key default nextval('product_pid_seq'),
    sku            text not null unique,
    name           text not null,
    description    text not null,
    quantity       int not null,
    cost           numeric(10,2) not null,
    price          numeric(10,2) not null,
    weight         numeric(10,2),
    status         product_status not null
);

插入产品的典型 Clojure 代码是:

(def prod-12345 {:sku "12345"
                 :name "My Product"
                 :description "yada yada yada"
                 :quantity 100
                 :cost 42.00
                 :price 59.00
                 :weight 0.3
                 :status "InStock"})

(sql/with-connection db-spec
   (sql/insert-record :product prod-12345))

但是,status它是一个枚举,因此如果不将其转换为枚举,就不能将其作为普通字符串插入:

'InStock'::product_status

我知道您可以使用准备好的声明来做到这一点,例如:

INSERT INTO product (name, status) VALUES (?, ?::product_status)

但是有没有办法在不使用准备好的语句的情况下做到这一点?

4

5 回答 5

3

我今天使用stringtype=unspecified hack解决方法得到了这个工作。

您可以将此参数添加到您db-spec的如下:

(def db-spec {:classname "org.postgresql.Driver"
              :subprotocol "postgresql"
              :subname "//myserver:5432/mydatabase"
              :user "myuser"
              :password "mypassword"
              :stringtype "unspecified"}) ; HACK to support enums

然后insert!照常使用。

最好有一个不会严重削弱类型安全的解决方案。

于 2014-02-13T11:19:19.460 回答
1

Kris Jurka用解决方法回复了上面引用的 Mike Sherrill 的讨论:

使用 url 参数 stringtype=unspecified [在 JDBC 连接 URL 中] 让 setString 始终绑定到 unknown 而不是 varchar,这样就不需要任何代码更改。

我在Java中试过这个,它似乎工作正常。

于 2014-02-12T19:55:38.383 回答
0

除非您将纯 SQL 传递到后端,否则您必须使用强制转换。(SQL 语句INSERT INTO product (name, status) VALUES ('SomeName', 'InStock');应该可以正常工作。)

在您提出问题一周后,Tom Lane 在 pgsql-hackers 上解决了这个问题。

AFAIK 这与 JDBC 一样正常:setString() 意味着参数是字符串类型。如果实际需要的类型不是字符串,它就会崩溃。(我不是 Java 专家,但我似乎记得使用 setObject 是标准的解决方法。)

枚举在这里没有遇到任何特别的困难,我反对削弱类型系统给他们一个特殊的通行证。

我们自己的@CraigRinger参与了该讨论,并且现在可能已经找到了一些相关的东西。

于 2013-05-09T01:16:27.990 回答
0

这篇博文很好地解决了这个问题。jdbc提供ISQLValue协议,它只有一个方法,sql-value将 clojure 值转换为 sql 值,由 a 表示PGObject。博客文章建议用表单中的关键字表示枚举:type/value,因此ISQLValue可以实现如下:

(defn kw->pgenum [kw]
  (let [type (-> (namespace kw)
                 (s/replace "-" "_"))
        value (name kw)]
    (doto (PGobject.)
      (.setType type)
      (.setValue value))))

(extend-type clojure.lang.Keyword
  jdbc/ISQLValue
  (sql-value [kw]
    (kw->pgenum kw)))

在您的示例中,您将插入您的产品:

(def prod-12345 {:sku "12345"
                 :name "My Product"
                 :description "yada yada yada"
                 :quantity 100
                 :cost 42.00
                 :price 59.00
                 :weight 0.3
                 ;; magic happens here
                 :status :product_status/InStock})

(sql/with-connection db-spec
   (sql/insert-record :product prod-12345))

问题是查询数据库时,枚举是一个简单的字符串而不是关键字。这可以通过实现IResultSetReadColumn协议以类似的方式解决:

(def +schema-enums+
  "A set of all PostgreSQL enums in schema.sql. Used to convert
  enum-values back into Clojure keywords."
  ;; add your other enums here
  #{"product_status"})

(extend-type java.lang.String
  jdbc/IResultSetReadColumn
  (result-set-read-column [val rsmeta idx]
    (let [type (.getColumnTypeName rsmeta idx)]
      (if (contains? +schema-enums+ type)
        (keyword (s/replace type "_" "-") val)
        val))))
于 2019-07-23T15:17:07.057 回答
0

如果有人在使用 clojure.java.jdbc、jdbc.next 的后继者时引用了这个问题,则插入枚举的代码类似于:

(ns whatever
 (:require
  [next.jdbc.sql :as jdbc.sql]
  [next.jdbc.types :as jdbc.types]
 ))

;; ...define your database connection and data source...    

(def prod-12345 {:sku "12345"
                 :name "My Product"
                 :description "yada yada yada"
                 :quantity 100
                 :cost 42.00
                 :price 59.00
                 :weight 0.3
                 :status (jdbc.types/as-other "InStock")})

(jdbc.sql/insert! ds :product prod-12345)

如“使用枚举类型”标题下记录的https://github.com/seancorfield/next-jdbc/blob/develop/doc/tips-and-tricks.md 。

于 2020-09-22T08:29:16.237 回答