How to conditionally include a Hibernate annotation? How to conditionally include a Hibernate annotation? mysql mysql

How to conditionally include a Hibernate annotation?


This answer attempts to implement Eugene's suggestion (so if it works please give credit to Eugene).

Given the following definition of @ifNotMysql macro

import scala.reflect.macros.blackboximport scala.language.experimental.macrosimport scala.annotation.{StaticAnnotation, compileTimeOnly}object ifNotMysqlMacro {  val targetIsMySql = sys.props.get("target-mysql").contains("true")  def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {    import c.universe._    def mysqlAnnots(annotations: Seq[c.universe.Tree]): Seq[c.universe.Tree] =      annotations        .filterNot(_.toString.contains("SequenceGenerator"))        .filterNot(_.toString.contains("GeneratedValue"))        .:+(q"""new GeneratedValue(strategy = GenerationType.IDENTITY)""")    val result = annottees.map(_.tree).toList match {      case q"@..$annots var $pat: $tpt = $expr" :: Nil =>        q"""            @..${if (targetIsMySql) mysqlAnnots(annots) else annots}            var $pat: $tpt = $expr          """    }    c.Expr[Any](result)  }}@compileTimeOnly("enable macro paradise to expand macro annotations")class ifNotMysql extends StaticAnnotation {  def macroTransform(annottees: Any*): Any = macro ifNotMysqlMacro.impl}

if we write @ifNotMysql @GeneratedValue(...) @SequenceGenerator like so

@ifNotMysql @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")@SequenceGenerator(name="generator", sequenceName = "cliSeq", allocationSize = 1)    var surrogateKey: Int = _

and provide system property target-mysql like so

sbt -Dtarget-mysql=true compile

then @SequenceGenerator annotation will be excluded and @GeneratedValue(strategy = GenerationType.IDENTITY) added like so

@GeneratedValue(strategy = GenerationType.IDENTITY)var surrogateKey: Int = _

This implementation is based on scalamacros/sbt-example-paradise


Probably not what you want to hear but AFAIK there's no way to conditionally include annotations. An alternative would be to include the common functionality in a @MappedSuperclass and inject the concrete instance as appropriate at build time depending on the environment. Something like this:-

@MappedSuperclassabstract class AbstractClientJpa {    var surrogateKey: Int   // abstract    var code: String = _    var name: String = _}

...

@Entity@Table(name = "clients")class HanaClientJpa extends AbstractClientJpa {    @Id    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")    @SequenceGenerator(name="generator", sequenceName = "cliSeq", allocationSize = 1)        var surrogateKey: Int = _}

...

@Entity@Table(name = "clients")class MySQLClientJpa extends AbstractClientJpa {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    var surrogateKey: Int = _}


Assuming I understand your problem correctly I have 2 potential solutions for you. Both are general ideas and you'll have to do some legwork to actually implement them.

  1. Use macros. Here is a bit old article that does some AST manipulation to enrich case classes. You should be able to do something in that vein for your case. Here is a way pass parameters to your macro at compile time. Main con with this route is that macro api was scala version dependent, somewhat messy, unstable and hard to find good documentation for last time I checked.

  2. Use AspectJ. You should be able to declare annotations you need on classes in build-time. Main con here is that you'll have to add AspectJ weaving to your build which may or may not be easy.