cayenne-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Frits Jalvingh <...@etc.to>
Subject Re: Big list'o questions.....
Date Sun, 02 Mar 2008 23:37:12 GMT
Hi Andrus,

Thanks for your swift reply!!!

> PreparedStatement caching can be configured outside Cayenne at the  
> DataSource level. Cayenne by itself doesn't do PreparedStatement  
> caching and doesn't care about it.
Check.
> 
> > 2. When Cayenne executes updates does it update the entire record or
> > does it only update the fields that changed? If it's the latter case:
> > can it be disabled to always update the full record (to allow
> > for statement caching - if available)?
> 
> The later. This is not configurable in Cayenne. I strongly suspect  
> that PreparedStatement caching is never a bottleneck in the case of  
> update (and in any event, if we issue a set of different UPDATE  
> queries for each entity, each variation can be cached just as well).  
> If somebody can prove me wrong with some profiling data, we can  
> reconsider the update approach.
Ok. It will be a bottleneck though for tasks doing lots of repetitive
work: preparing a statement in Oracle easily takes 100x (!!) as much
time as executing it. And sending lots of different statements to the
DataSource's cache does not work- it caches only some of them to prevent
open cursor troubles so if you send it too many it does not work. 
Finally, sending lots of statements to Oracle means the database's SQL
cache (SGA) which holds the database's cached execution plans will loose
efficiency. This will add up to quite large costs when the application
is used by many people.

This means I would not use Cayenne for any kind of work that handles
large amounts of objects; something that is not advisable anyway for the
current generation of ORM's.

If I decide to go with Cayenne I'll do some database profiling.

> > 3. The documentation states that when you call rollbackChanges() a
> > transaction the DataContext state is restored to the state it had
> > before. Does it do this by using "copies" of the objects it has read
> > or is the database re-queried?
> 
> Rollback is a local operation. No DB access is performed.
> This requires a bit of explanation. While rollback operation does not  
> perform any DB queries, modified objects are turned into "hollow"  
> objects. I.e. they will be re-read from the DB lazily on the first  
> access... Now, with DataDomain object snapshot caching underneath 
> the DataContext, this still means no DB access in most cases. 
> Still some internal Cayenne activity will happen to restore the state.
Crystal clear ;-) This means you have no "previous" state in memory to
restore the objects to pristine state?

> 
> > 4. What happens if data gets commited to the database and a  
> > SQLException
> > occurs (like "duplicate key", "null in non-null column" etc?
> > Specifically:
> > 4.a Is the state of the DataContext once such an error occurs still
> > usable?
> 
> Yes.
That's great!!

> 
> > 4.b Are the values of a persistent object in that DataContext changed
> > during the commit or rolled back to some earlier state?
> 
> No, but the user has a choice to do that if he wants.
> 
> > 4.c Would it be possible to "fix" the values on just the objects that
> > "failed" in the same context and commit again?
> 
> Yes.
That looks just great! Way better than Hibernate which completely
buggers it's internal state after an exception. This makes it hell to
code for proper error handling in the UI.

> 
> > 5. When using "nested" DataContext, if you commit to the "upper"
> > DataContext I expect that the changes are only propagated to the  
> > Object
> > Store in that upper context, not to the database. Is that correct?
> 
> It can be done both ways. 'commitChangesToParent' vs. 'commitChanges'.
Ok. Just to be clear: would commitChanges() commit all of the changes
including the changes of all parents into the database? Or does it only
commit the changes in the child context to the DB and updates the parent
context?

> 
> > 6. The current implementation uses generated base classes to contain
> > Cayenne-specific code. JPA implementations need something like
> > bytecode-generation (proxies) to add code to persistent classes
> > as JPA-defined classes need no platform-specific base class. So I  
> > assume
> > that the JPA variant of Cayenne will have something like that.
> 
> Yes.
> 
> > Are there
> > plans to extend this to the Cayenne-specific interface?
> > I would not want to use JPA because it sucks bigtime, but having clean
> > classes is a wish.
> 
> (enhanced) POJO without JPA is in the plans (and partially works  
> already). But this is not a high priority.
Check.

> 
> > 7. The project is currently busy supporting the JPA standard. Is it  
> > the
> > vision of this project to become a JPA provider mainly and deprecate  
> > the
> > own interfaces?
> 
> No, absolutely no plans to deprecate Cayenne API. The goal is to keep  
> both API's on top of the same (existing) Cayenne stack as first class  
> citizens.
Ah, good.

> 
> > 8. If I would select Cayenne for my project I would need to add some
> > features to it. The reason is that I will access an already-existing
> > legacy database with lots of oddities like triggers and stuff.
> > Specifically:
> > a. I would need to add an alternative version of "Optimistic locking"
> > where a specific field in the database would hold a timestamp or  
> > version
> > number. This number should be maintained by Cayenne i.e. incremented  
> > as
> > soon as Cayenne inserts/updates the record. It would also be used in  
> > the
> > where for updates/deletes exactly like the current optimistic locking
> > mechanism to check for changes at that time.
> 
> We are implementing an auto-incrementing version as a part of JPA  
> alignment. So this is coming.
> 
> > b. I would need support for database-generated fields at update and
> > insert time. I must be able to mark fields as "generated at update,
> > generated at insert or both". Cayenne should retrieve these fields
> > after inserting/updating a record to keep the Object representation
> > in-sync with the database. For some databases this would require a
> > "select after update";
> 
> You can achieve that with JPA-like lifecycle callbacks (that are  
> available in Cayenne outside JPA) - POST_UPDATE, POST_PERSIST.
By "refreshing" the objects in such a callback? That might be doable
then; I would like a declarative way better though because I would need
to code every separate record's generated fields into such a handler...

> 
> > other databases (Oracle, Postgresql) can return
> > the changed fields in the same statement (insert xxxx returning yyy).
> 
> Hmm... does it get passed over JDBC back to the client, and if so, do  
> you have a JDBC example? (I know that even the standard callback via  
> 'Statement.getGeneratedKeys()' still doesn't work with many drivers).
Sure, I used it in a proof-of-concept ORM that I built to check whether
it was possible to build something more reasonable than Hibernate or
JPA. It does depend on the database, but the ones I use (Oracle,
Postgresql) both support it albeit in slightly different ways.

For Oracle you need to use a CallableStatement instead of a
PreparedStatement and create a statement like:

begin
  update xxx set ..... where .... returning [columnname],
[columnname]...;
end;

Before calling CallableStatement.execute() you must call
CallableStatement.registerOutput() with the SQLType of each returned
column.

After calling you can retrieve the values using
CallableStatement.getXXX(). This is very fast for Oracle; way faster
than Hibernate's "update-then-reselect". I also used it to support
native Oracle primary keys using sequences; you can generate inserts the
same way using the sequence in the values list, like:

begin
  insert into xxx(id, a, b...) values(my_sequence.nextval, ?, ?, ...)
    returning id;
end 

which is again faster than selecting the sequence in a separate
statement.

Postgresql supports something much alike but uses a PreparedStatement,
no begin/end, and to retrieve the data you need to call getResultSet()
on the preparedstatement; the resultset contains the data from the
returning clause. I did not check the performance of this although the
Postgresql documentation leads me to believe it is a single roundtrip.

The sad thing is that this makes database adapter classes rather complex
because lots of things need to be delegated there to support these quite
different ways of supporting native sequences and quick returns of
updated data. The advantage is that performance is way better.

All of this, plus using bytecode instead of reflection makes my
proof-of-concept code easily outperform Hibernate. If only completing
(and maintaining!!) it wasn't such a lot of work ;-)....

If you need a concrete example I can cook one up.

> > c. Both these changes require that all generated fields get copied and
> > stored within a database commit so that the original values (before  
> > the
> > commit) can be restored when flushing data to the database fails; this
> > is needed to keep the DataContext consistent after a commit failure.
> 
> As far as restoring the DataContext state, Cayenne should be able to  
> that.
Ok.

Again, thanks for your fast answers! I'm going to look further into
Cayenne; your answers spared me lots of time!

Frits
> 
> Andrus


Mime
View raw message