DataMapper完成了许多无用的查询

时间:2012-01-15 17:23:54

标签: sinatra datamapper

我遇到了DataMapper的问题(我正在和Sinatra一起使用)

我有一个非常基本的应用程序有3个模型。 这是代码。

class Level
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true, :unique => true, :lazy => true
  property :description, Text, :lazy => true
  timestamps :at
end

class Player
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true, :lazy => true
  timestamps :at
  belongs_to :game
end

class Game
  include DataMapper::Resource
  property :id, Serial
  has n, :players
  belongs_to :level
  belongs_to :current_player, 'Player', :required => false
end

这是一条基本路线:

get '/' do
  DataMapper::logger.debug 'Creating level'
  level = Level.create(:name => "One")

  DataMapper::logger.debug 'Creating game'
  game = Game.create(:level => level)

  DataMapper::logger.debug 'Adding players'
  alice = Player.create(:name => 'Alice', :game => game)
  bob = Player.create(:name => 'Bob', :game => game)

  DataMapper::logger.debug 'Setting game current player'
  game.current_player = alice
  game.save
  'ok'
end

我的问题是,当我查看DataMapper日志文件时,我发现它已经做了很多无用的查询,我不明白为什么!

这是日志输出:

 ~ Creating level
 ~ (0.000062) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.002241) INSERT INTO "levels" ("name", "created_at", "updated_at") VALUES ('One', '2012-01-15T18:15:28+01:00', '2012-01-15T18:15:28+01:00')
 ~ Creating game
 ~ (0.000048) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001747) INSERT INTO "games" ("level_id") VALUES (1)
 ~ Adding players
 ~ (0.000050) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.003762) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Alice', '2012-01-15T18:15:28+01:00', '2012-01-15T18:15:28+01:00', 1)
 ~ (0.000085) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001820) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Bob', '2012-01-15T18:15:28+01:00', '2012-01-15T18:15:28+01:00', 1)
 ~ Setting game current player
 ~ (0.000078) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001826) UPDATE "games" SET "current_player_id" = 1 WHERE "id" = 1

正如您所看到的,对于关卡模型有很多疑问。我真的不明白为什么DataMapper会这样做。

非常感谢您的帮助。

PS:您可能认为这不是什么大问题但我在发布之前实际上简化了模型结构。实际模型更复杂,并且充满了那些无用的查询。

这是我的真实数据映射器日志文件的一小部分: 当我保存我的游戏模型实例时会发生这种情况。

 ~ (0.001640) UPDATE "asd_games" SET "updated_at" = '2012-01-15T17:51:27+01:00', "current_player_id" = 3, "current_action_id" = 3 WHERE "id" = 1
 ~ (0.000079) SELECT "id", "body" FROM "asd_actions" WHERE "id" = 3 ORDER BY "id"
 ~ (0.000083) SELECT "id", "name", "description" FROM "asd_levels" WHERE "id" = 1 ORDER BY "id"
 ~ (0.000057) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000075) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000083) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000082) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.000084) SELECT "id" FROM "asd_levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1

2 个答案:

答案 0 :(得分:0)

正在进行额外的SELECTS检查:unique => true类的Level约束。这项检查似乎是在每次数据库调用时进行的。

避免这种情况的一种方法是在创建模型对象时使用create,而不是立即将模型保存在数据库中,使用new然后通过一次调用保存整个对象图在合适的对象准备就绪后到save(参见docs on creating and saving models):

DataMapper::logger.debug 'Creating level'
level = Level.new(:name => "One")

DataMapper::logger.debug 'Creating game'
game = Game.new(:level => level)

DataMapper::logger.debug 'Adding players'
alice = Player.new(:name => 'Alice', :game => game)
bob = Player.new(:name => 'Bob', :game => game)

DataMapper::logger.debug 'Setting game current player'
game.current_player = alice
game.save

产生输出:

 ~ Creating level
 ~ Creating game
 ~ Adding players
 ~ Setting game current player
 ~ (0.000074) SELECT "id" FROM "levels" WHERE "name" = 'One' ORDER BY "id" LIMIT 1
 ~ (0.001062) INSERT INTO "levels" ("name", "created_at", "updated_at") VALUES ('One', '2012-01-15T20:07:16+00:00', '2012-01-15T20:07:16+00:00')
 ~ (0.001460) INSERT INTO "games" ("level_id") VALUES (1)
 ~ (0.001279) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Alice', '2012-01-15T20:07:16+00:00', '2012-01-15T20:07:16+00:00', 1)
 ~ (0.001592) UPDATE "games" SET "current_player_id" = 1 WHERE "id" = 1

因此模型不会立即保留,但都是一起完成的,唯一性检查只进行一次。

另一种可能性是在:auto_validation => false属性上设置:name

此更改产生此输出(使用create):

 ~ Creating level
 ~ (0.001162) INSERT INTO "levels" ("name", "created_at", "updated_at") VALUES ('One', '2012-01-15T20:13:51+00:00', '2012-01-15T20:13:51+00:00')
 ~ Creating game
 ~ (0.001958) INSERT INTO "games" ("level_id") VALUES (1)
 ~ Adding players
 ~ (0.001194) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Alice', '2012-01-15T20:13:51+00:00', '2012-01-15T20:13:51+00:00', 1)
 ~ (0.001304) INSERT INTO "players" ("name", "created_at", "updated_at", "game_id") VALUES ('Bob', '2012-01-15T20:13:51+00:00', '2012-01-15T20:13:51+00:00', 1)
 ~ Setting game current player
 ~ (0.001369) UPDATE "games" SET "current_player_id" = 1 WHERE "id" = 1

所以仍然有多次调用数据库,但是没有在每次调用时进行检查(实际上它看起来根本就不是这样做的,所以这相当于使用{{1}的对象失败了首先)。

答案 1 :(得分:0)

我遇到了同样的问题,我有一个用户和一个工作。作业属于用户。片段:

class User
  include DataMapper::Resource
  property :id,                 Serial,   writer: :protected, key: true
  property :email,              String,   required: true, length: (5..40),
                                          unique: true, format: :email_address
end

class Job
  include DataMapper::Resource
  property :id, Serial, key: true
  property :progress, Integer
  property :updated_at, DateTime
  belongs_to :user
end

每次保存作业时,都会运行两个查询:

~ (0.000421) SELECT `id` FROM `users` WHERE `email` = 'mike@example.com' ORDER BY `id` LIMIT 1
~ (0.001589) UPDATE `jobs` SET `progress` = 19, `updated_at` = '2013-12-19 06:32:43' WHERE `id` = 91

因为我确信我的更新不包含任何错误数据(没有任何东西被用户输入所污染),所以我能够通过使用保存的bang(!)版本来停止SELECT的运行

-    save
+    save!

http://datamapper.org/docs/create_and_destroy.html描述了当您使用bang方法而不是非爆炸方法时实际发生的情况,因此您需要查看它并查看它是否对您的用例是安全的。