简体   繁体   English

Play2的anorm无法在postgresql上运行

[英]Play2's anorm can't work on postgresql

I found that the row parsers of play2's anorm depend on the meta data returned by jdbc driver. 我发现play2的anorm的行解析器依赖于jdbc驱动程序返回的元数据。

So in the built-in sample "zentasks" provided by play, I can find such code: 所以在play提供的内置示例“zentasks”中,我可以找到这样的代码:

object Project {
  val simple = {
    get[Pk[Long]]("project.id") ~
    get[String]("project.folder") ~
    get[String]("project.name") map {
      case id~folder~name => Project(id, folder, name)
    }
  }
}

Please notice that the fields all have a project. 请注意,这些字段都有一个project. prefix. 字首。

It works well on h2 database, but not on postgresql. 它适用于h2数据库,但不适用于postgresql。 If I use portgresql, I should write it as: 如果我使用portgresql,我应该把它写成:

object Project {
  val simple = {
    get[Pk[Long]]("id") ~
    get[String]("folder") ~
    get[String]("name") map {
      case id~folder~name => Project(id, folder, name)
    }
  }
}

I've asked this in play's google group , and Guillaume Bort said: 在Play的谷歌小组中问过这个问题 ,Guillaume Bort说:

Yes if you are using postgres it's probably the cause. 是的,如果你使用postgres它可能是原因。 The postgresql jdbc driver is broken and doesn't return table names. postgresql jdbc驱动程序已损坏,并且不返回表名。

If the postgresql's jdbc driver really have this issue, I think there will be a problem for anorm: If two tables have fields with the same name, and I query them with join , anorm won't get the correct values , since it can't find out which name belongs to which table. 如果postgresql的jdbc驱动程序确实存在这个问题,我认为会出现anorm的问题: 如果两个表的字段名称相同,并且我使用join查询它们,则anorm将无法获得正确的值 ,因为它可以'找出哪个名称属于哪个表。

So I write a test. 所以我写了一个测试。

1. create tables on postgresql 1.在postgresql上创建表

create table a (
    id      text not null primary key,
    name    text not null
);

create table b (
    id      text not null primary key,
    name    text not null,
    a_id    text,
    foreign key(a_id) references a(id) on delete cascade
);

2. create anorm models 2.创建anorm模型

case class A(id: Pk[String] = NotAssigned, name: String)
case class B(id: Pk[String] = NotAssigned, name: String, aId: String)

object A {
  val simple = {
    get[Pk[String]]("id") ~
      get[String]("name") map {
        case id ~ name =>
          A(id, name)
      }
  }

  def create(a: A): A = {
    DB.withConnection { implicit connection =>
      val id = newId()
      SQL("""
          insert into a (id, name)
          values (
            {id}, {name}
          )
          """).on('id -> id, 'name -> a.name).executeUpdate()
      a.copy(id = Id(id))
    }
  }

  def findAll(): Seq[(A, B)] = {
    DB.withConnection { implicit conn =>
      SQL("""
          select a.*, b.* from a as a left join b as b on a.id=b.a_id
          """).as(A.simple ~ B.simple map {
        case a ~ b => a -> b
      } *)
    }
  }
}

object B {
  val simple = {
    get[Pk[String]]("id") ~
      get[String]("name") ~
      get[String]("a_id") map {
        case id ~ name ~ aId =>
          B(id, name, aId)
      }
  }

  def create(b: B): B = {
    DB.withConnection { implicit conneciton =>
      val id = UUID.randomUUID().toString
      SQL("""
          insert into b (id, name, a_id) 
          values (
          {id}, {name}, {aId}
          )
          """).on('id -> id, 'name -> b.name, 'aId -> b.aId).executeUpdate()
      b.copy(id = Id(id))
    }
  }
}

3. test cases with scalatest 3.使用scalatest的测试用例

class ABTest extends DbSuite {

  "AB" should "get one-to-many" in {
    running(fakeApp) {
      val a = A.create(A(name = "AAA"))
      val b1 = B.create(B(name = "BBB1", aId = a.id.get))
      val b2 = B.create(B(name = "BBB2", aId = a.id.get))

      val ab = A.findAll()
      ab foreach {
        case (a, b) => {
          println("a: " + a)
          println("b: " + b)
        }
      }
    }
  }
}

4. the output 4.输出

a: A(dbc52793-0f6f-4910-a954-940e508aab26,BBB1)
b: B(dbc52793-0f6f-4910-a954-940e508aab26,BBB1,4a66ebe7-536e-4bd5-b1bd-08f022650f1f)
a: A(d1bc8520-b4d1-40f1-af92-52b3bfe50e9f,BBB2)
b: B(d1bc8520-b4d1-40f1-af92-52b3bfe50e9f,BBB2,4a66ebe7-536e-4bd5-b1bd-08f022650f1f)

You can see that the "a"s have name of "BBB1/BBB2", but not "AAA". 您可以看到“a”的名称为“BBB1 / BBB2”,但不是“AAA”。

I tried to redefine the parsers with prefixes as: 我试图用前缀重新定义解析器:

 val simple = {
    get[Pk[String]]("a.id") ~
      get[String]("a.name") map {
        case id ~ name =>
          A(id, name)
      }
  }

But it will report errors that they can't find specified fields. 但它会报告无法找到指定字段的错误。

Is it a big issue of anorm? 这是一个大问题吗? Or do I miss something? 或者我会错过什么?

The latest play2(RC3) has solved this problem by checking the class name of meta object: 最新的play2(RC3)通过检查元对象的类名来解决这个问题:

// HACK FOR POSTGRES
if (meta.getClass.getName.startsWith("org.postgresql.")) {
  meta.asInstanceOf[{ def getBaseTableName(i: Int): String }].getBaseTableName(i)
} else {
  meta.getTableName(i)
}

But be careful if you want to use it with p6spy , it doesn't work because the class name of meta will be "com.p6spy....", not "org.postgresql....". 但要小心,如果你想与p6spy一起使用它,它不起作用,因为meta的类名称将是“com.p6spy ....”,而不是“org.postgresql ....”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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