简体   繁体   中英

How to create Anorm query to skip updating None values in DB (Scala)

I am using Anorm (2.5.1) in my Play+Scala application (2.5.x, 2.11.11). I keep facing the issue quite often where if the case class argument value is None, I don't want that parameter value to be inserted/updated in SQL DB. For example:

case class EditableUser(
    user_pk: String,
    country: Option[String],
    country_phone_code: Option[Int],
    phonenumber: Option[String],
    emailid: Option[String],
    format_all: Option[String]
)
....
val eUser: EditableUser = EditableUser("PK0001", None, None, None, Some("xyz@email.com"), Some("yes"))
...
    SQL"""
       update #$USR SET
       COUNTRY=${eUser.country},
       COUNTRY_PHONE_CODE=${eUser.country_phone_code},
       PHONENUMBER=${eUser.phonenumber},
       EMAILID=${emailid},
       FORMAT_ALL=${format_all}
       where (lower(USER_PK)=lower(${eUser.user_pk}))
    """.execute()

Here when the value is None, Anorm will insert 'null' into corresponding column in SQL DB. Instead I want to write the query in such a way that Anorm skips updating those values which are None ie does not overwrite.

You should use boundStatements/preparedStatement and while setting values for the query don't set the values for the columns which are none.

For example

SQL(
  """
select * from Country c 
join CountryLanguage l on l.CountryCode = c.Code 
where c.code = {countryCode};
  """
).on("countryCode" -> "FRA")

Or in your case:

import play.api.db.DB
import anorm._

val stat = DB.withConnection(implicit c =>
  SQL("SELECT name, email FROM user WHERE id={id}").on("id" -> 42)
)

While writing you your query you check if the value you are going to put in on(x->something) is not None if it's nice don't put it hence you will not update the values which are none.

Without the ability (or library) to access the attribute names themselves, it would still be possible, if slightly clunky in some circles, to build the update statement dynamically depending on the values that are present in the case class:

case class Foo(name:String, age:Option[Int], heightCm:Option[Int])
...
def phrase(k:String,v:Option[Int]):String=if (v.isDefined) s", $k={$k}" else ""

def update(foo:Foo) : Either[String, Foo] = DB.withConnection { implicit c =>
  def stmt(foo:Foo) = "update foo set "+
    //-- non option fields
    "name={name}" +
    //-- option fields
    phrase("age", foo.age) +
    phrase("heightCm", foo.heightCm)

  SQL(stmt(foo))
    .on('name -> name, 'age -> age, 'heightCm -> heightCm)
    .executeUpdate()

The symbols that are not present in the actual submitted SQL can still be specified in the on . Catering for other data types also needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM