I have a problem with DataMapper (I’m using it with Sinatra)
I have a very basic app with 3 models.
Here’s the code.
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
Here’s a basic route:
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
My problem is that when I look at the DataMapper log file, I find it has made many useless queries and I don’t understand why!
Here’s the log output:
~ 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
As you can see, there’s a lot of queries for the level model. I really don’t understand why DataMapper is doing these.
Thanks a lot in advance for your help.
PS: You may think that it is not a big deal but I actually simplified the model structure before posting here. The actual model is more complex and is full of those useless queries..
Here’s a short part of my real datamapper log file:
It happens when I save an instance of my game model.
~ (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
The extra
SELECTSare being made to check the:unique => trueconstraint on theLevelclass. This check seems to be being made on every database call.One way to avoid this would be instead of using
createwhen creating your model objects, which immediately saves the model in the database, usenewand then save the whole object graph with a single call tosaveon a suitable object when they’re all ready (see the docs on creating and saving models):produces the output:
So the models are not immediately persisted, but are all done together, and the uniqueness check is only done once.
Another possibility would be to set
:auto_validation => falseon the:nameproperty.This change produces this output (using
create):So there are still multiple calls to the database, but the check isn’t made on each call (in fact it doesn’t look like it’s being made at all, so this rather defeats the object of using
:unique => truein the first place).