例如,这是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)
但是有没有办法在不使用准备好的声明的情况下做到这一点?
答案 0 :(得分:2)
今天我使用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!
。
如果有一个不会过分削弱类型安全性的解决方案,那将是一件好事。
答案 1 :(得分:1)
Kris Jurka replied讨论Mike Sherrill上面提到的解决方法:
使用url参数stringtype = unspecified [在JDBC连接URL中]使setString始终绑定到unknown而不是varchar,然后不需要任何代码更改。
我在Java中试过这个,看起来效果很好。
答案 2 :(得分:0)
除非您将纯SQL传递给后端,否则您将不得不使用强制转换。 (SQL语句INSERT INTO product (name, status) VALUES ('SomeName', 'InStock');
应该可以正常工作。)
Tom Lane addressed this issue。
AFAIK这与JDBC一样正常:setString()暗示着 参数是字符串类型。实际上,如果这种类型,它将会失败 必需的只是一个字符串。 (我不是Java专家,但我似乎 回想一下,使用setObject代替标准的解决方法。)
Enums在这里没有任何特殊的困难,我会反对 弱化类型系统给他们一个特殊的传递。
我们自己的@CraigRinger participated in that discussion,现在可能已找到相关内容。
答案 3 :(得分:0)
This blog post很好地解决了这个问题。 jdbc
提供了ISQLValue
protocol,它只有一种方法sql-value
,该方法将clojure值转换为由PGObject
表示的sql值。该博客文章建议使用: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
protocol来以类似的方式解决此问题:
(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))))
答案 4 :(得分: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标题下的“使用枚举类型”所述。