{"id":48,"date":"2014-06-19T20:12:09","date_gmt":"2014-06-19T20:12:09","guid":{"rendered":"http:\/\/www.mbarsinai.com\/blog\/?p=48"},"modified":"2014-06-20T02:24:42","modified_gmt":"2014-06-20T02:24:42","slug":"anorm-and-duplicate-column-names","status":"publish","type":"post","link":"https:\/\/www.mbarsinai.com\/blog\/2014\/06\/19\/anorm-and-duplicate-column-names\/","title":{"rendered":"Anorm and Duplicate Column Names"},"content":{"rendered":"<p>A bit of a technical post, just in case someone else hits this problem, e.g. while working through <a href=\"http:\/\/www.manning.com\/hilton\/\" title=\"Book's page at Manning.com\">Play for Scala<\/a> book (which I heartily recommend). That&#8217;s how I stumbled on this issue.<\/p>\n<p><a href=\"http:\/\/www.playframework.com\/documentation\/2.0\/ScalaAnorm\" target=\"_blank\">Anorm<\/a> is an interesting alternative to ORMs. This is pretty much going against the flow of &#8220;let us write your SQL for you&#8221;, so they have some explainin&#8217; to do. Anorm sticks to native SQL, and make it easier to parse result rows into objects. One way of making it easy are SQL row parser combinators.<\/p>\n<p>The idea is this: parsers take column values from an SQL result row, and return a Scala object. Combining the parsers yields tuples, that can later be parsed into Scala objects (normally, some case class).<\/p>\n<p>So that&#8217;s all fine and dandy, but the code below produces unwanted results:<\/p>\n<pre><code>\r\nval prodsAndItems =  SQL(\"\"\"\r\n    SELECT p.*, s.* FROM products p INNER JOIN stock_items s \r\n    ON p.id = s.product_id\r\n    ORDER BY p.id\r\n    \"\"\")\r\n\r\n  val productParser: RowParser[Product] = {\r\n    long(\"id\")~long(\"ean\")~str(\"name\")~str(\"description\") map {\r\n      case id~ean~name~description => Product(id, ean, name, description)\r\n    }\r\n  }\r\n  \r\n  val productsParser: ResultSetParser[List[Product]] = { \r\n    productParser *\r\n  }\r\n\r\n  val stockItemParser: RowParser[StockItem] = {\r\n    long(\"id\")~long(\"product_id\")~long(\"warehouse_id\")~long(\"quantity\") map {\r\n      case id~prodId~wareHouseId~quantity => StockItem(id,prodId,wareHouseId,quantity)\r\n    }\r\n  }\r\n\r\n  val stockItemsParser: ResultSetParser[List[StockItem]] = {\r\n    stockItemParser*\r\n  }\r\n  \r\n  val productStockItemParser: RowParser[(Product, StockItem)] = {\r\n    productParser ~ stockItemParser map (flatten)\r\n  }\r\n\r\n  def selectAllProductsAndItems: Map[Product,Seq[StockItem]] = DB.withConnection{ implicit c =>\r\n    val res:List[(Product, StockItem)] = prodsAndItems.as( productStockItemParser * )\r\n    res.groupBy( _._1 ).mapValues( _.map(_._2) )\r\n  }\r\n<\/code><\/pre>\n<p>This is a classic case of products, warehouses and stock items (product quantities that are stocked in warehouses). You would expect the call to <code> selectAllProductsAndItems<\/code> to return a map object mapping products to their stock items. Instead, it returns a set of new products that don&#8217;t exist in the database, and maps a single stock item to each one.<\/p>\n<p>Now, I love creating new products. I just don&#8217;t want my data access layer to do it for me. Enough of this &#8220;machines replacing people&#8221; zeitgeist.<\/p>\n<p>Why was Anorm doing this? (spoiler alert from here on)<br \/>\nBoth <code>stock_items<\/code> and <code>products<\/code> have a column called &#8220;id&#8221;. The basic parsers (defined in <code> stockItemParser<\/code> and <code>productParser<\/code>) call the columns by their names, and so get ambiguous results.<\/p>\n<p>The solution turns out to be pretty simple (as always) &#8211; reference the column by its full name: &#8220;products.id&#8221; instead of &#8220;id&#8221;. Over to you, Peg:<\/p>\n<p><span class=\"embed-youtube\" style=\"text-align:center; display: block;\"><iframe loading=\"lazy\" class=\"youtube-player\" width=\"625\" height=\"352\" src=\"https:\/\/www.youtube.com\/embed\/e3mLoFndR6M?version=3&#038;rel=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;fs=1&#038;hl=en-US&#038;autohide=2&#038;start=3&#038;wmode=transparent\" allowfullscreen=\"true\" style=\"border:0;\" sandbox=\"allow-scripts allow-same-origin allow-popups allow-presentation allow-popups-to-escape-sandbox\"><\/iframe><\/span><\/p>\n<p>P.S <a href=\"https:\/\/github.com\/michbarsinai\/PlayForScalaBook\/commit\/aaa8d52a3c1ac92d50a97a873b6fd8cd0da0aa3d\" target=\"_blank\">commit link on github<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A bit of a technical post, just in case someone else hits this problem, e.g. while working through Play for Scala book (which I heartily recommend). That&#8217;s how I stumbled on this issue. Anorm is an interesting alternative to ORMs. This is pretty much going against the flow of &#8220;let us write your SQL for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[3,2],"tags":[7,6,8,13],"class_list":["post-48","post","type-post","status-publish","format-standard","hentry","category-programming","category-scala","tag-anorm","tag-play","tag-problem","tag-scala"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p3NnQg-M","_links":{"self":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts\/48","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/comments?post=48"}],"version-history":[{"count":10,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts\/48\/revisions"}],"predecessor-version":[{"id":58,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts\/48\/revisions\/58"}],"wp:attachment":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/media?parent=48"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/categories?post=48"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/tags?post=48"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}