sql

All things SQL and JDBC...
Asko Nõmm 2020-11-17T10:42:33.358200Z

Thank you @dharrigan and @seancorfield for the help yesterday, I’ve now re-written my code given your suggestions and I no longer get timed out errors. Everything works smooth as butter, and I think I actually gained performance improvements.

dharrigan 2020-11-17T10:43:22.358700Z

You're most welcome. Feel free to shout up if you need any further help! 🙂

athomasoriginal 2020-11-17T15:06:43.362500Z

When querying data from the DB across multiple tables jdbc.next will use those table names as the qualified name . For example, a get-product ,query might return the following

{:product/id   v
 :price/amount v
 :product/name v
 :user/creator v}
My question is what do people think about renaming the qualified names to all be something like product/* instead of the original table name? Is there value in this?

athomasoriginal 2020-11-17T15:07:53.363600Z

The rationale I thought of was: • avoid leaking implementation details • easier for consumers to reference the keys • support evolution of backend (contrived example: maybe amount is derived from two tables in the future)

seancorfield 2020-11-17T18:02:56.364900Z

@tkjone As the documentation says: try to work with the default behavior for a while before you start fighting against it.

seancorfield 2020-11-17T18:03:33.365500Z

Hash maps with a mix of namespace qualifiers are perfectly idiomatic Clojure. People just aren't used to it yet.

seancorfield 2020-11-17T18:05:30.366800Z

The additional clarity is worth the effort, I assure you, partly because it makes you think more carefully about refactorings. It also makes any transformation you do between layers very explicit.

seancorfield 2020-11-17T18:06:54.368500Z

If you tell next.jdbc to use a fixed :qualifier-fn (such as using :product/*) then you are still leaking your DB structure and/or query structure into your code but now you've disguised part of that, instead of thinking carefully about structures, layers, and boundaries.

athomasoriginal 2020-11-17T19:13:25.372900Z

Fair point! Do you feel these points also apply to the HTTP API layer (e.g. REST)? That is the boundary I was grappling with. For example

(get-products)  ;; returns mixed namespace qualifiers

;; --- boundary ----

(defn http-handler []
  (get-products)

(http-router
  ["api/products" http-handler])  ;; should this return mixed namespace qualifiers to consumers of the REST service?

seancorfield 2020-11-17T19:34:48.376Z

@tkjone Most APIs have to traffic in unqualified names (for JSON interop etc) so I would expressly transform any internal data into the appropriate external format at that last stage. If you're using JSON, Cheshire and clojure.data.json both have options to produce unqualified keys (but they have opposite defaults: one maintains qualifiers by default, the other does not).

seancorfield 2020-11-17T19:37:39.377800Z

My rule of thumb is: only transform qualified keys if you have to. So I tend to leave DB results as qualified/snake_case, for example, up to the point where I need them in an alternative format -- but often that "need" is a system boundary of some sort.

seancorfield 2020-11-17T19:39:54.380400Z

In some situations, I have a very explicitly "domain model format" that uses specific qualified/kebab-case names and I'll transform to/from other_qualified/snake_case at the boundary of that domain model code, but where it's "just data" as opposed to some specific entity I let it flow through the system (e.g., reporting against "log" tables and CRUD-based admin functionality all tends to maintain the DB-centric qualified/snake_case names).

athomasoriginal 2020-11-17T19:42:28.381Z

Thanks @seancorfield!

seancorfield 2020-11-17T19:43:03.381600Z

(I'm afraid I'm somewhat notorious for my "it depends" answers when asked for specific guidelines 🙂 )

😆 1
athomasoriginal 2020-11-17T19:44:46.382800Z

haha in this case it answered my main question and aligned to how i’m doing things as is, except now i’m confident with performing the transform at the boundary.

1
Asko Nõmm 2020-11-17T20:37:53.386300Z

When using honeysql with next.jdbc, and only selecting info from a single table at a time, is it still normal to get namespaces keywords back e.g :table/column? It was my understanding that it would only be the case if I selected info from multiple tables in one query. Not that there’s anything wrong with it, but it’s just sort of of odd to write stuff like (get post :posts/title) where I would instead like to write (get post :title) . A sample code I use is:

(-> :select :*
    :from :posts
    sql/build
    sql/format)
And then I simply pass the result to next.jdbc’s execute!

athomasoriginal 2020-11-17T20:39:42.386700Z

Yep 🙂

athomasoriginal 2020-11-17T20:40:10.387300Z

This is partially the conversation sean and I we’re having above.

Asko Nõmm 2020-11-17T20:41:54.388300Z

Yup I read it and understood it made sense if selecting info from multiple tables, but I’m not doing that, which makes me feel like maybe I’m doing something wrong (or maybe not)

Asko Nõmm 2020-11-17T20:42:31.389100Z

I’m guessing I have to use :qualifier-fn to get a desired result then?

👍 1
athomasoriginal 2020-11-17T20:43:37.389900Z

You’re not doing anything wrong. It’s the default behaviour for jdbc.next

Asko Nõmm 2020-11-17T21:02:02.390800Z

I really have to get better at reading the docs 😄 I finally found that :builder-fn as-unqualified-maps does the trick perfectly.

seancorfield 2020-11-17T21:48:40.391400Z

@asko I'd strongly recommend working with the default behavior for a while and using qualified keywords -- they are idiomatic Clojure.

seancorfield 2020-11-17T21:49:45.391900Z

:posts/title is, after all, semantically different to :people/title

seancorfield 2020-11-17T21:50:07.392400Z

(the latter being things like "Mr.", "Ms.")

seancorfield 2020-11-17T21:51:17.393Z

There's a reason that as-unqualified-maps is not the default behavior and is not introduced until later 🙂

seancorfield 2020-11-17T21:52:18.393200Z

"Relying on the default result set builder -- and table-qualified column names -- is the recommended approach to take"

dharrigan 2020-11-17T22:03:17.395400Z

Yes, I too took the initial approach of using as-unqualified-maps, but I'm now using the better approach of namespaced keywords, and slowly going back to fix the stuff that did use as-unqualified-maps to use namespaced ones. It does pay off in the long run to adopt Sean's good advice there 😉

dharrigan 2020-11-17T22:15:08.396300Z

(I've updated my simple startrek repo to show how I remove the namespace, as in my simple contrived example I don't need it, but in more complicated programs, I do keep the namespace and work with it 🙂 )