Play2 的异常无法在 postgresql 上运行

发布于 2025-01-04 21:57:52 字数 3952 浏览 6 评论 0原文

我发现play2的anorm的行解析器依赖于jdbc驱动程序返回的元数据。

所以在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)
    }
  }
}

请注意,这些字段都有一个 project. 前缀。

它在 h2 数据库上运行良好,但在 postgresql 上运行不佳。如果我使用 portgresql,我应该将其写为:

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

我已询问 此内容位于 Play 的 Google 群组,以及纪尧姆·博尔特说道:

是的,如果您使用的是 postgres,这可能就是原因。 PostgreSQL jdbc 驱动程序已损坏并且不返回表名称。

如果postgresql的jdbc驱动真的有这个问题,我认为anorm会有问题: 如果两个表具有相同名称的字段,并且我使用 join 查询它们,anorm 将无法获得正确的值,因为它无法找出属于哪个名称到哪张桌子。

所以我写了一个测试。

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. 创建异常模型

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. 使用 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. 输出

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)

您可以看到“a”的名称为“BBB1/BBB2”,但不是“AAA”。

我尝试将带有前缀的解析器重新定义为:

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

但它会报告找不到指定字段的错误。

这是异常的大问题吗?或者我错过了什么?

I found that the row parsers of play2's anorm depend on the meta data returned by jdbc driver.

So in the built-in sample "zentasks" provided by play, I can find such code:

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. prefix.

It works well on h2 database, but not on postgresql. If I use portgresql, I should write it as:

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:

Yes if you are using postgres it's probably the cause. The postgresql
jdbc driver is broken and doesn't return table names.

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.

So I write a test.

1. create tables on 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

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

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

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".

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

别低头,皇冠会掉 2025-01-11 21:57:52

最新的play2(RC3)通过检查meta对象的类名解决了这个问题:

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

但是要小心,如果你想和p6spy一起使用它,它不起作用,因为meta的类名会是“com.p6spy ....”,而不是“org.postgresql ....”。

The latest play2(RC3) has solved this problem by checking the class name of meta object:

// 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....".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文