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: Chuck Hill <email@hidden>
- Date: Wed, 23 Jan 2008 13:19:11 -0800
On Jan 23, 2008, at 12:06 PM, David Elliott wrote:
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.
I defer the ones on join tables. EOF can update these out of sync
when relationships between two objects are removed and restored. So
it can do the insert before the delete. Of course, EOF could be
fixed to do the ordering properly...
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.
Mmmm. Mike is the person that _I_ ask when I have a question on the
deep down EOF internals. I'm pretty sure he "gets" it.
Chuck
--
Practical WebObjects - for developers who want to increase their
overall knowledge of WebObjects or who are trying to solve specific
problems.
http://www.global-village.net/products/practical_webobjects
_______________________________________________
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