Re: Why doesn't EOF sort our PK problems itself ?
Re: Why doesn't EOF sort our PK problems itself ?
- Subject: Re: Why doesn't EOF sort our PK problems itself ?
- From: David Elliott <email@hidden>
- Date: Wed, 23 Jan 2008 15:06:40 -0500
Hi Mike,
Note that I am not the one complaining about the PK problem, only
saying that it's possible to fix it.
On Jan 23, 2008, at 2:19 PM, Mike Schrag wrote:
On failed save EOF does not update the global IDs with integer
PKs. Do something trivial like adding a null-allowed column in EO
but making nulls not allowed in the DB. Attempt to save. When it
fails change the DB to allow nulls or fill in the property that
cannot be null. Save again. You'll notice that the PKs grabbed
for the first attempt are skipped and that EOF will ask for PKs
again and will use those.
You might be able to wrap your saveChanges in a loop until it
succeeds, but you'd need to intercept and parse the duplicate
primary key exception, which would be db-specific. What I don't
know is if you have deferred constraints if that also includes PK
conflicts, and if so, would you only get a single exception on save
and not know which item it actually correlated with (or even that
it was a dupe PK error -- every db sends this back in different
ways, and some don't even support codes on these, so you literally
have to parse the exception messages, which is a disaster in the
making). Definitely a lot of unfun things you would likely have to
do to try and get this to work, which is why I think the better
solution is to fix pk generation period and support autoincrement
columns in the first place (so you wouldn't even have to deal with
this). Not to say that's a trivial change by any means, but EOF
really should be capable of using them. That eo_pk_table thing
always felt like a huge hack to me.
You're correct in thinking such a fix would be a DB-specific thing.
One advantage though is that at least as far as I know, PK
constraints are rarely deferred as there is no reason I know of to do
so. At least with Posstgres, PK constraints cannot be deferred as
far as I know. It completely defeats the point of a primary key.
Also, you ought to know why EO does not use auto-increment PKs in the
first place. To do so would require a huge rethink of the way the
EOAccess layer operates. At the moment it grabs all of the new PKs
from the DB in one piece of code and then actually issues the changes
in another piece.
With auto-increment PKs you would not know the PK until after you did
the insert. That means that the PK propagation phase would have to
be done concurrently with the insertion phase. Right now that's not
possible because the inserts aren't ordered in any particular
manner. For instance, what do you do when you've got a hierarchical
structure with one PK propagating into a compound PK on another entity?
Worse yet, what do you do when you have cyclical dependencies? One
pattern I use is to relate Foo to-many Bar but then also have an FK
in Foo for the Bar that is most current (e.g. a cache of sorts). The
way EOF works now the PK propagation happens before any inserts so
EOF can generate a PK for Foo and a PK for all of the Bar records,
propagate the Foo PK into the foo FK in each Bar and propagate the
one Bar's PK into the Foo FK for the most current Bar.
It is impossible to do this if you defer PK creation until the insert
phase. A hack would be to insert Foo with a null active_bar_id, then
insert all of the bar records, then update foo's active_bar_id. But
the question then becomes how to figure this out automatically. It's
a very non-trivial problem that EOF already solves by grabbing unique
PKs and propagating them itself before inserting records.
A slightly different design is to make Bar's PK compound such that it
is composed of (foo_id,i). In that case one has to manually assign
Bar's "i" component but it does make it simpler in the sense that Foo
then only has an active_bar_i which is known even earlier. Careful
though, don't make the activeBar relationship a class property or
you'll kill foo_id and not just active_bar_i if activeBar is null.
That's of course trivial to work around by synthesizing the activeBar
relationship by knowing Foo (which is this after all) and the
activeBarI which you can use to pull the right bars object.
Anyway, the bottom line is that EOF's style of PK generation is a
feature, not a bug. It's very difficult to understand why EOF does
things the way it does until you hit one of these cyclical cases and
then you finally get it. In fact, these cyclical cases are not
uncommon at all and it's one of the things that non-EOF developers
fight with. A typical solution is to move to GUID keys.
There's already an example of some auto-PK fixes in the
PostgresqlPlugin. Should the sequence object not exist, the
plugin will recreate it. Furthermore, should it exist but be
returning IDs that are too low then assuming you catch adaptor
exceptions you can try saving again and eventually you'll bump the
sequence high enough. You can also stop after the first save,
regenerate primary key support, then save again and it will pull
new PKs.
This is different, though ... This is a failure in the pk
generator, which is pretty straightforward to catch and handle in
the plugin. The problem you're talking about is a failure during
commit, after the plugin's pk generator has already run.
That is true. But it's still before EOF commits to actually using
the specific key values. So it's possible to correct these
problems. Assuming PK constraints are not deferred then regardless
of database you should get an exception immediately on the insert
line. Then you get into a situation where you need to figure out why
you got the exception. That's DB dependent but on a DB like Postgres
it will be a failure on the index which has a well-known name. So
simply grepping the error message for the index name should be
sufficient for postgres and a similar solution can be used for most
other databases.
Do I think this is something that ought to go into EOF proper? Not
really. But the original poster was looking for a way to
automatically fix his PK sequences and so I'm giving him the outline
of how he could do this. He may decide (and I would if I were him)
that ultimately it's cheaper to remind yourself to update the PK
sequence if you make changes outside of EOF.
So clearly it's not impossible to add some code that upon primary
key conflicts will simply regenerate the PK support. One drawback
is that while you are regenerating the PK support you really ought
to block all other access to the database or else you could easily
get two DB clients trying to do the regeneration concurrently.
Nothing's _impossible_ :), but I suspect it is pretty tricky to get
it to work right. You basically have all the same context the
framework does at this point, though. I suspect you could try to
prototype this in your own code at a higher level to see if there's
even enough info available to pull it off.
I assure you there's enough information to pull it off, but it would
have to be done very deep in the EOAccess layer, probably via a
delegate, and doing it requires a very deep understanding of EOF and
a very deep understanding of why EOF works the way it does. Every
problem with PKs in EOF translates to a lack of understanding on the
part of the developer as to how EOF does its thing. The best
reference on this stuff is Chuck and Sacha's book with the bumble-bee
cover but even that does not (believe it or not) go deep enough.
-Dave
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden