Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2002-712
$eventId
ID of event: wwdc2002
$eventContentId
ID of session without event part: 712
$eventShortId
Shortened ID of event: wwdc02
$year
Year of session: 2002
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC02 • Session 712

Advanced Enterprise Objects Frameworks

WebObjects • 59:56

This session provides an in-depth exploration of the advanced features of Enterprise Objects Framework (EOF). Topics include inheritance, delegate methods, shared editing contexts, raw rows, and data synchronization.

Speakers: Steve Miner, Ben Trumbull

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

So I wanted to welcome you all to our last session of the day, which is Advanced Enterprise Objects Frameworks. And we have Steve Minehr here to present for you. Steve? Thank you, Toni. So today's session is Advanced EOF. My name is Steve Minehr. I'm a Web Objects Engineer. Helping with the demos later will be Ben Trumbull, another Web Objects Engineer. So we'll bring him up a little bit later.

First, before we get started, I'd like to know how many people attended the Intro to EOF session on Tuesday? I saw James Dempsey. So he put on a great show. I'm sorry I don't have any musical ability. No singing or dancing for you today. But if you do come to the beer bash tonight, there'll be a band there. So I hope that takes care of the entertainment.

Can I also ask, I wonder how many people here are new to EF or new to WebObjects? Okay, so there's a few. How many people feel like they're advanced users? You prefer to edit your models in Emacs because it's faster? All right, good, good. We got a couple.

All right, so I'll try to give you something new. A lot of this is going to be review of things you picked up in other sessions. But we'll try to leave time for questions at the end because I have a feeling most people come to these sessions just to ask questions. They don't really want the content.

Okay, so introduction, we're going to talk about a number of different topics in EOF. We've already reviewed models in this previous session. I'm going to give you my spin on a couple of little things. We'll talk about designing EOs, and I'll give you some recommendations there. I'm going to strongly recommend you use single-table inheritance when you're using inheritance. We'll get to that. Most of that was already explained in the last session.

We'll do a quick bit on shared editing contexts, and we might get some questions on that later. I'm going to go over delegate methods, but I don't have enough time, and to tell you the truth, I can't get into every single delegate, so I'm going to give you my general spiel on delegate methods and what to look for.

Then we'll get to data synchronization and have a demo there. That's an issue that affects some large installations when you have multiple WebObjects instances or you have multiple writers, and there's some interesting issues there. Finally, I'm going to talk about the use of the EOF. I'm going to talk about the use of the EOF. I'm going to talk about the use of the EOF. I'm going to talk about the use of the EOF. Finally, if we have time, we'll talk about raw rows and using SQL within EOF.

Okay, before I get started into my talk, I want to review last year's talk. Eric Noyo gave a really good talk. It had the same title, Advanced EOF. I'm stealing only a couple of his slides. You should go back and look at last year's talk if you want to review. Eric told us about his bug tracker example.

And one of the big points he made was the separation of the user interface from the controller logic. And he had a nice framework for doing his controller and showing you how you can change that to put different UIs on the same application. Eric also made a point of doing built-in testing while you're developing.

This is one of the ideas from extreme programming. You might not be so extreme, but it is a good idea to build some tests into your components and into your application as you're developing it. Then you could feel adventurous, make big changes, run your test, make sure you didn't break anything else. and Eric's example is still available on the web, so you can download that and play with that.

Okay, the technology frameworks that we deal with in EOF, of course we start with Java Foundation. That's all your base classes and a lot of utility classes. We build on that with Java EO Control. That's where EO Editing Context lives and Fetch Specifications. That's where you're going to do most of your programming when you're dealing with EOF.

Java EO Access is our database access layer. For the most part, you don't need to know how EO Access works, but since you came to an advanced talk, I'm going to talk about it. And similarly, with our JDBC adapter, at the API level, you shouldn't have the program to the JDBC adapter. We mention it just because that's the main framework you want to include when you're talking to relational databases, and we add that automatically for you. in Project Builder.

All right, I stole this slide from another talk because I thought it was a pretty slide and I didn't have any great graphics in mind. EOF is that little part over the right. Okay, most of this is other stuff in WebObjects, but the part on the right is the database talking to your web application machines.

So here's another slide I just stole from the previous talk. But I'll take credit for this last year. I designed this slide, so I gave it to them. The idea here is editing context at the top. That's where you're doing most of your programming. If you're new to EOF, spend time looking at EO editing context. Look at the API. Make sure you understand how to add objects to both sides of relationships.

There's plenty of API there. It's not too complicated. But that's where you want to concentrate your time and effort. All right? I'm going to talk about some other things. I don't want to confuse new people, but advanced people sometimes run into issues where they have to dig a little deeper into the EOF stack.

The object store coordinator is a simple object that coordinates multiple object stores. And an object store is kind of an abstract representation of some data source out there in the real world. Normally, your object store is going to be an EO database context. That's our object that knows how to manage dealing with any particular database.

and EODatabaseContext is a complicated class with lots of delegates. There's plenty of opportunities for you to take control of EOF there. Normally you don't have to, but it's there if you need it. EODatabase uses, I'm sorry, EODatabaseContext uses an EODatabase to store snapshots. So that maintains the EOF's idea of what data exists in the database.

We use those, those are row level snapshots. We use that for checking objects when we're going in and out of the database. When we're instantiating objects, we're sharing that memory that's stored in the snapshots. The, at the intro course, James mentioned that I talked more about snapshots, so I'll talk about it now because I didn't have a slide for this. But EOeditingContext also keeps snapshots. It keeps object level snapshots.

So you can imagine any EO can more or less be translated in terms of storage. It keeps the storage at state into a dictionary. So that's what a snapshot is. It's just a dictionary representation of the state of an EO. And EOeditingContext will keep track of that as you're changing EOs. There's a last committed snapshot. You can always take a current snapshot of any EO and store that away.

The EODatabase keeps row level snapshots. So again, it's a dictionary idea, but their values in the EODatabase correspond to the columns in your database. So it's a little bit different. It's not necessarily your full EO. And you might have more information there. The EODatabase snapshots, for example, have primary key information, whereas your EOs typically won't have primary key information in your snapshots at the editing context level.

The database channel controls the interaction with the database, issuing queries to the database. And each of these database sorts of objects has an adapter object corresponding to it that it uses to deal with a particular database. In our case, we have a JDBC adapter, so there's a JDBC adapter context and there's a JDBC channel.

Those particular data sources are the ones that are used to deal with the EODatabase. Those particular adapter level classes know how to deal with a particular data source. So we're specialized for JDBC with our JDBC channel. The database channel object is more abstract and it just uses the adapter channel to get the job done. And then below, of course, there's a relational database.

All right, for model recommendations, Justin talked about quite a bit of this before, so I'll go quickly. In the primary key, it's simpler. If you use a simple energy as your primary key, there was a question last time about compound keys. That's no problem, but you have to do a little bit more work if you have compound primary keys. All your relationships are a little bit more complicated. That's fine. Many DBAs will set up the database for you that way. Complicated models are natural.

If you have a big enterprise application. But if you're in control and you're designing the database from scratch just for your WebObjects application, I recommend that you keep it simple. Use an integer as your primary key. And usually, you don't want to use that primary key as a foreign key into another table. If you're doing inheritance, that's natural. You have multiple tables. They share the primary key. You can take the primary key from one table to get into the other.

But in the general case, you'll want to have a separate attribute, which is your foreign key, into the other table. That's going to make it more flexible for you and will allow you to have optional relationships where that foreign key value might be null. Whereas some people get into trouble where they want to make an optional relationship, but they're joining using the primary key, which is identically the same as the primary key in the other table. And EOF will be confused by that because any time you have a value for a foreign key, it expects to see another row in the other table. And if you don't actually have that other row there, you're going to get an error.

We were talking about class properties. Justin mentioned to be careful with large to many's. You don't always have to model your relationships as class properties. You might have them in your model so that you can form queries over them, but you might not need those values every time you instantiate Neo.

So the idea here is just a question of performance and being pragmatic. There may be cases where you don't need that to many to be updated every time you update the inverse relationship. So leave it off, don't turn it on as a class property. When you need those values, you can use a fetch spec.

You can fetch all those objects across that relationship on demand. This is a question of how large the to many is. Similarly, for, well, for another reason, primary keys usually are not marked as class properties. You usually don't need to manipulate the primary key. But if you have some complicated situation where your primary key is not marked as class property, you usually don't need to manipulate the primary key. But if you have some complicated situation where your primary key isn't a simple integer, then you might want to populate that yourself. But of course, once you have Neo constructed with a primary key value, you're not allowed to change that.

When you're modeling blobs, the recommendation is to put the blobs in a separate table. And this, again, was a question in the last session. EOF is creating faults. I'll talk about faults a little bit more for you in a minute. But a fault is basically an empty shell of an object. We'll fill that up later. If your blobs are in your main table and their class properties, whenever EOF fetches that object, it has to instantiate the blob in memory.

If it's in a separate table and you use a relationship to the other EO containing just the blob, then that allows us to make that a fault. And that can give you better performance in the situation where you're not necessarily touching all those blobs. And as Justin mentioned, you probably don't want to lock on blobs. There's no SQL command for us to do a comparison against the blob, so we have to pull the whole thing into memory, compare it in memory, and that's usually a performance killer.

When we're talking about generating primary keys, the adapter will automatically generate primary keys for you if they're simple integers. If there's any other kind of primary key, then you need to fill that in yourself. And one way you can do that is with the delegate method, database context, new primary key.

You can implement that whenever EOF is inserting an object in the database, it will ask your delegate to give it, fill out a value for the primary key dictionary and then use those values into the, put those values into the database for you. Or if you mark your primary key as a class property, you can implement wake from insertion on your EO, fill in that value whenever you insert an EO, you'll be responsible for giving it a unique primary key.

There's also one other way that we can do it automatically, it's not on the slide, but if your primary key is an NSData of exactly 24 bytes, EOF has a magic way of coming up with a globally unique value for those 25 bytes. That's a little complicated, but if you want to use it, go ahead.

All right, when you're designing EOs, the first thing, of course, you have to take a look at the EO Enterprise Object Interface. But for most people, you don't want to just implement that interface yourself. You want to use one of our base classes. First base class is EO Custom Object. That's your basic EO. It knows how to do everything with faults. And again, let me review faults while we're here.

A fault is an empty shell of an EO. The reason we create faults is when we're fetching the first EO and it has relationships to other EOs, you can imagine a long chain of relationships. We're pulling in one EO. We don't want to pull in the whole database or follow that long chain on the first fetch. So EOF creates an empty shell, a placeholder, for that object. And when your code is going to touch that object, EOF will populate it.

Now, the situation can be more complicated with batch faulting. We can actually populate multiple objects at the same time when you fire one fault. We'll fill a few more of them, so that can help you with performance and improve the locality of reference there. But the key thing on faults is that in your getter methods, you need to call will read. If you're implementing EO custom object, it's a very easy requirement, but just remember this. In all your getter methods, call will read first. That gives the framework a chance to populate the fault, create a real EO, so that everything else will work for you.

Another requirement is in your setting methods, your setters have to call will change. Will change will call will read, so that takes care of the faulting part of it. And then will change also allows the framework to keep track of EOs that have changed. And then as we mentioned in all the other sessions, EOF is keeping track of all those changed objects, and later when you save changes, we know which ones to send back to the database for you.

Now if you don't want to write any code, you can use EOGenericRecord. And so you can think of this as your EO with values and keys in it. So it's your, it's the simplest way to do your programs. It's a great way to get started and EOModeler will use EOGenericRecord if it can't find your class or you don't specify a class in your EOModel.

There's no code required. We do automatic storage. It's actually a tiny bit faster in that we're controlling all the storage. The key value coding runs a little faster. Another nice feature is EOGENERIC_RECORD can do deferred faulting. So I just talked about normal faulting where we create a shell of an object, we'll fill it in later. Deferred faulting doesn't even allocate the memory for the object yet.

It's deferring even creating the fault. We have a special unique placeholder object. And because this is all happening within our framework, we can take care of creating the fault and creating the real object when it's actually needed. So you can save a little bit of memory with deferred faulting.

If you want, your EOCustom objects can also implement deferred faulting. That's another interface. You'll need to call one extra method in your accessor methods to make sure that deferred faulting is working. So it's really simpler probably to use EOGENERIC_RECORD. You can subclass EOGENERIC_RECORD and when EOModeler creates a Java class for you by our templates, we're creating a subclass of EOGENERIC_RECORD. You can add IVARs there and do whatever you want. And you'll see how the EOGENERIC_RECORD accesses values using the stored value accessors a little bit later.

All right, last part of designing your EOs. You want to keep your EO code clean. This is really just a basic object-oriented programming principle of encapsulation. Don't put more code in your EOs than they need to know about. Make sure you just use Java Foundation and EO control concepts in your EOs. You shouldn't be using EO access. Maybe you might want to use EO utilities. That's kind of a special area of EO access.

It's, um, some convenience, uh, functionality there for, um...

[Transcript missing]

All right, key value coding. Probably you've dealt with key value coding if you've done any work in WebObjects Builder or any EOF applications before. The interfaces for key value coding are spread across four different interfaces there that I've listed. The basics are pretty simple. If you want to, you can override it. We don't recommend you do, but it's all there if you want to take control yourself.

I would suggest take a look at the inner classes of each of those interfaces. It has an inner class called utility and default implementation. The utility has static methods that if you're taking control, you can invoke these static methods on any object. We'll check to make sure we do the right thing. And the default implementation has our performance optimizations in it. So even if you're doing something fancy, try to call back into our default implementation. But for most users, you don't need to change key value coding. You're just going to be using it.

So the basics, access to your object by key is to call value for key. So on any EO, if you know your different attribute names, you can get the value by calling value for key. Take value for key is for setting a value. And we'll explain exactly how those methods work in a minute.

Value for key paths and take value for key paths is just an extension. Instead of having a simple key, you can have a key path, which is a chain of keys. So you can be following a relationship, and that's the notation for that is just a string with a key separated by dots.

One interesting item is that NSDictionary implements key value coding. So you can see the correspondence between snapshots, which are NSDictionary's, and EOs allow you to kind of mix and match values out of EOs and dictionaries. NSArray is special and to many relationships are implemented with NSMutable arrays. They have special keys that they understand these at sign operators.

At sign count gives you the count of the array. The other operators, min, max, sum, and average, all take another key after them. So you're using a key path and you might say, give me the average of a particular key in all these EOs that were in an array. and we'll return that single value.

The stored value accessors, these are the more primitive way of getting access to EOs. And these methods are mainly used by our framework in order to, say, populate EOs, take stored value for key. When we're fetching the raw data out of the database, the framework is going to invoke take stored value for key to populate your EO object.

So we're trying to do it in the most primitive way possible. We'll talk about the difference between the stored value and the take, and the regular value for key in a minute. Values for keys and take values from dictionary are methods that let you slice and dice between dictionaries and EOs.

So you can update your EOs from a dictionary or you can pull out just pieces of your dictionary by using values, pieces of your EO by using values for keys. And then these last two static methods you can implement on your EO class and it controls how this, how we do our key value coding.

First of all, whether or not we can access your member fields directly, usually that's true and we'll use your IVARs directly, but you can set that to false if you don't want key value coding to look into your EOs and hit the IVARs. And the other option is should use stored accessors, and that controls whether or not we're going to do anything special on the stored value accessor methods. We'll talk about that in a minute.

So we're going to do a little bit of a quick review of the EO class. We're going to talk about the EO class, and then we're going to talk about the EO class. And then we're going to talk about the EO class. And then we're going to talk about the EO class. And then we're We'll talk about that in a second.

So quickly, value for key, that's the main way that you're going to access values out of your EOs. We have to look up a method, figure out how to get that value out of your EO. And the way we do that is looking for the public method first.

If we can't find a public method, then we're going to look for an underbar method. And the convention is the underbar method is maybe a little bit more private or more direct. And finally, if there's no method matching that key name, we'll look for a field matching the key name. And so you can see the whole list of things we'll look at there.

Stored Value for Key just changes the order that we're going to look for a way to access your EO. And as we said before, the stored The stored value methods are trying to get to the more primitive values, bypassing some of your code maybe. You might have your public method implement something a little more complicated than your underbar primitive method.

So you can take advantage of this when you're coding. If you have to do something complicated, put your public API that you want other users to invoke in your regular get method, and then maybe have an underbar get name that does something a little more direct to your EO. And of course, we'll hit the fields, and finally, if there's no underbar or field, we'll call your public method.

Take value for key. This is how we're setting things, so we're looking for the set methods. If we don't find a method, we'll look for the underbar, and finally we'll hit the field directly. And when we're doing the take stored value, we just change that order. We're hitting the primitive underbar method first, otherwise the field, and then the public method. So you don't have to do anything fancy, but if you do have a complicated EO and there's some subtlety to how you want that EO populated versus how you want the rest of the world to access the EO, you can take advantage of following this convention.

and its validation is our interface declaring how we validate EOs. We don't want you to put the validation code in the setter methods. We want you to write a separate method on a particular key. You can call our method validate value for key. We'll call your custom validate key.

So we'll show you in a second an implementation of that. Again, you can override and do something different in validation. Take a look at the interclass utility so you can invoke this on any object. And also take a look at our default implementation. Normally you don't want to override this, but it's there if you want to.

Here's a validation example. If you have a key name, then you might write a method. In this case, we're taking a string and returning a string. If something goes wrong, then we want to throw an exception. And our framework is looking for that exception. It's an NSValidation validation exception. In this case, it's very simple. We're just saying we want the length of the name to be greater than four. We'll throw if it's not. Otherwise, we'll just return the name. But you might do something more sophisticated.

All right, you can also do EO validation. These validation methods are sent to your EO, and you can validate the entire EO, not just an individual key or attribute on that EO. So, the names are kind of self-explanatory. When your EO is first being inserted in an editing context, we'll call validate for insert. When you're updating that EO, we can call validate for update. When we're saving changes, we'll call validate for save. And finally, if you delete the EO, we'll call validate for delete. So, you can put extra business logic in any of these methods.

A few other methods that you might consider implementing on your EO to handle unknown keys. These are really error handling. There's an interface called error handling. Handle query with unbound key. So if you send your EO some key it doesn't know about, if you implement this method, we'll call this method and give you a chance to recover from that or do something special. And similarly for handle take value for unbound key.

Unable to set null for key. Some attributes don't allow null. You want to maybe substitute a zero or some other special value. You can do that using this method. Awake from insertion. Whenever you're inserted into the EO, I'm sorry, whenever you're inserted into the EO editing context, your EO will get called with this method, awake from insertion. If you're populating your own primary keys, that's a good place to do it. When the EO is fetched out of the database, it will be sent the message awake from fetch. So you can do some special processing there to fix up your EO.

Okay, the following methods are things that you want to invoke but you don't want to override. These are all things used by the framework and we give you a good implementation and I don't think anything good can happen if you override any of these methods. So, will change notifies the editing context that you're changing this object. Do that in your setter methods. Will read in your getter methods. Will read relationship if you're implementing deferred faulting, you'll need to invoke this.

Again, if you're doing an eogeneric record, you don't even have to worry about invoking any of these methods because we'll invoke those for you. Then the stored value access, stored value for key and take stored value for key are kind of private to our implementation. We'll invoke those at the right time. You can invoke those if you're subclassing eogeneric record, but I don't think you should ever override those.

Okay, we'll switch gears a little bit and talk about inheritance. This was covered in the last session, so this is a bit of review. But vertical inheritance is nice because it's natural for object-oriented programming. It's simple maintenance. You can add an attribute at any parent entity and all the other entities will inherit that attribute.

You don't have to do much maintenance on your database table because there's only one table where you have to add another column. But the fetches are expensive because we have to do joins and we have to do multiple fetches to do deep inheritance fetching because we have to fetch once for every sub-entity.

Horizontal inheritance, where you duplicate the column names in different tables, is a bit more efficient in that we don't have to join when we're doing fetches for the simple case. But there's still more maintenance overhead. If you want to update your database or your object model and add another attribute, you have to remember to go put that attribute in all the subentities that inherit from the same parent.

So the recommendation is to use single table inheritance. In single table inheritance, your columns are the union of all the columns needed by all your subentities. Each subentity might map a different set of those columns. The downside is you have to allow null in anything that's not common to all the subentities.

We'll have to insert a null in those columns that aren't used by a particular subentity row. The big advantage is we can do a single fetch for deep inheritance. So we only have to issue one select, get all the values out of the table, and then we can assemble those into the correct kind of subentity class.

And this also avoids a limitation on sorting. When you're doing horizontal inheritance and you're trying to sort, we're actually issuing multiple select statements to the database. Each of those is sorted, but you don't have an overall sorted result. So that's a little bit of a problem in horizontal inheritance. Single table inheritance, of course, since we're doing one select, you'll get the natural result back.

Then you remember this from the previous slides. The idea here is that everybody's sitting in one table. I'm sorry, that was from the previous session. Now, we say we can do single table inheritance fetching in one select, but that's only if you meet a few restrictions on how you set up your single table inheritance.

First requirement is that you have some attribute that's distinguished to determine what sub-edit this particular row is. And the simplest way to do that is to just have a subtype column, maybe have different constants assigned for each sub-edity. Sub-edity one has a one in that column, two, and so on. And you write a restricting qualifier, which is of the form attribute equals some constant value.

Okay, this, if you follow these two simple requirements, then EOF can do a single fetch and

[Transcript missing]

All right, so if you're trying to-- if you're implementing single table inheritance, typically you'll have to implement a wake from insertion. Whenever you create a new instance of your EO, you'll want to set this column.

So if the attribute was called sub, you have a method called setSub, and then each unique sub entity-- each sub entity has a unique implementation of mySub that just returns that constant value. and once you've set the value for the determinant of the subentity, you shouldn't change it. That's almost like a primary key. It's write once.

All right, new topic, shared editing context. I don't have a lot to say about shared editing context. It was touched on in the previous session. In your EO model, you can designate fetch specifications that will fetch objects whenever we load this model into the shared editing context. So the shared editing context is some common space that your other editing contexts kind of get to hold onto by default.

And the idea here is you might fetch these things once it's started up of your application. You never have to fetch them again. Everybody can see those EOs. Maybe you have a catalog application, some other kind of mostly read-only permanent data. Put it in your shared editing context. But there's a requirement that other editing contexts can have relationships that point into your shared set of EOs, but none of the shared EOs can point out to the other editing contexts.

While we're here, I should also point out that we are aware of a bug that is causing some kind of deadlocking issue when people are using shared editing contexts. So we're working on that. We have our top people on that. And I don't know exactly when it will be fixed, but just to be aware of that, we'll get on it.

Delegate Methods. So, EOF is very customizable. There are lots of places you can implement delegates, but New users probably don't want to look at delegates first. This is what you look at when you can't figure out why isn't EOF doing what I want. There must be a better way. There probably is if you go take control and use a delegate.

But when you implement a delegate, remember you're kind of doing brain surgery on the EOF stack, so you have a lot of responsibility to do the right thing in your delegate method. And your delegate methods then get invoked possibly fairly often, so you want to make sure it's efficient.

In the EO Control Layer, take a look at EO Editing Context. There's a few delegates there controlling how updates are merged. And each of these classes on the list here has an inner class called Delegate. So you can explore that in our API, our Java documentation, and see what other delegates are available. EO Database Context has quite a few delegates. You can control a lot of what EO Database Context is doing in terms of fetching and dealing with the database there. EO Adapter Context also has some delegates that you might be interested in.

All these classes in the EO Access layer, though, are a little difficult for your application to get a hold of. They're created on demand when you first start doing something that requires you to go to the database. You usually have easy access to your editing context. Your session has a default editing context, or you've created it yourself.

But you don't really have direct access to your database context, your EO database context, or these other things that we're creating on demand. So there's a static method in each of these classes called setDefaultDelegate. This lets you start your application early on in your application. You can call setDefaultDelegate.

Whenever EO database context or any of these other things are created after that point, it will automatically be assigned the delegate you wanted. So this gives you a way to kind of preload your delegates. It makes it much easier than trying to reach into the stack and setting a delegate.

Data synchronization. So this is an issue that comes up especially in large deployments. First thing to remember is EOF is a big cache. So we fetched values out of the database. We checked when we're writing back that values haven't changed. But sometimes if you have multiple WebObjects application instances, you might have multiple writers or you might even have external writers, some other application that has access to your database.

And the values have changed out from underneath your EO. Then when we try to do an update and write to the database, we're comparing against the old values. EOF will notice that something has changed, which means you have stale data, and will throw an exception, and you'll get an optimistic locking exception.

So, that's an issue that requires some coding. Let's talk about what's happening at UF. This graphic again shows you on the right hand side, you can see you have multiple web optics, application instances. They may be talking to multiple databases, but the important thing is each WOAP might have a slightly different view of the data because of updates and the time that things were fetched. And if we have an optimistic locking failure, we'll have to do something about it. So, what are the approaches to handling optimistic locking failure? Well, the first thing, of course, do nothing. We've conveniently implemented that for you in the framework.

Okay. We do nothing. It's a little bit complicated. It is application-specific about how to handle that. But for a lot of applications, you're mostly read-only, so this issue doesn't come up. In many other applications, even though you're writing data to the database, it's partitioned data. Another user doesn't necessarily have to see the data that's being written. So again, in those kind of situations, you don't have to worry about this. But if you're in a real concurrent situation, you have multiple writers, then you have to do something. So there's two ideas. One is try to avoid the optimistic locking failure.

And the other thing to do is to recover from it. And if you want a robust application, maybe you want Let's talk about avoidance. So the issue here is just, sometimes it's just a matter of freshness of your data. EOF is trying to cache data, so maybe you fetched something a long time ago and you wanted to update it and get the latest data from the database.

One way to do that is to refault objects. So you can tell your editing context to refault objects for you. The editing context also has a notion of a fetch timestamp. Fetch timestamp is the editing context request for freshness of the data. So when any context is first built by default, it will accept data from this object store that I think the default is one hour old.

And that's reasonable for a lot of applications. You might want to change that to you only want data that's one minute old. That's acceptable, or even less. But the tighter you set the fetch timestamp, the more fetching you'll have to do. So you have to think about that a bit.

When you're fetching objects on your fetch specifications, as was shown in the last session, you can set refreshes, refetched objects, which usually I can't say in one go. You can set that so that...

[Transcript missing]

But it is useful if you know there are triggers out in the database or something else strange has happened.

So that's your best efforts if you're trying to avoid changes. You can also use Dave Newman's Change Notification Framework. So this is not something we ship with WebObjects, but Dave Newman from Apple has made this available to a number of people. Wirehose recently shipped, and they have an improved version of it that is available for download.

The idea here is that you want to notify your other WO apps that you've made changes in one WO app. You send those changes to the other WO apps so that they try to keep in sync. So this has been used in several customer applications, and people are pretty happy with it. So you can check that out. Download it from wirehose.com.

Okay, finally, you may have tried to avoid optimistic locking failure, but somebody else got in there, changed the data. Now you want to recover from it. All right, so you need to catch this EO General Adapter exception. Then you're going to have to drill down a bit and investigate what happened. Take a look at the user info. There are a number of constants defined in EO Adapter Channel.

First, there's the look at adapter failure key. Check that it's equal to the adapter optimistic locking failure. Then you'll have another key, failed adapter operator key. Take a look at that in the user dictionary, and there'll be all kinds of information about what went wrong, what your snapshots were, what EO you were dealing with, and you'll have a chance to recover. Now, that doesn't help you so much, but we have a demo coming up. I'd like to invite Ben Trumbull to come up. So Ben has implemented this, and he'll show you how to recover.

So basically what happened is I'm sort of simulating another user or database administrator just whacking on your table. So there's a background process that's going, and it's just picking random rows out of a table and just changing them. So that's going to cause some problems, as you might imagine.

The big thing that you need to do is... As we come in here, Steve was talking about this general adapter exception that you need to catch. And you need to take a look at this key and check to see if it's an optimistic locking failure. And then I just sort of isolated the exact code down in here. It's a little bit more than you might need so I can show you exactly what happened so I can make you believe that I actually did something. Otherwise, the save just goes through.

So, let's see We get the operation type, which is going to be an integer and their constants, and we're going to do a switch down here on which one. And we get the adapter operation and the database operation. And then from the database operation, we find out what EO failed.

And from the adapter operation, we find out what changes we were trying to make when we saved. And we also get a hold of the database snapshot and stuff so we can show that in our success page. Then we switch on this. This app's pretty simple, and all it does is doing updates. So we ignore deletes, and we don't do any explicit locking.

So we kind of gloss over that. As Steve mentioned, this is application-specific. There isn't any right answer. So what I've done here is implemented a last right wins behavior. And that may or may not be what you want. In fact, as I'll show you in the demo, it produces some behavior you might consider odd depending on who you want to be right, whether or not you want the first guy to be right or the last guy to be right.

And basically we refault the object, and then we reapply the changes dictionary we got out of the adapter operation, and then we try to save again. Unfortunately, you're only going to get one exception. So if you do a save and you save, you know, 20 EOs, or actually in this example we saved about 14 rows all at once.

and three or four of them have been changed. You're going to get an exception one at a time. That's just the way the EO database context goes through and performs each adapter for each global ID. So you could expect to possibly have to catch a number of these, which is why we've put this in a while loop.

And we keep saving until we either get an adapter exception that's not an optimist clocking failure, in which case we don't really know what to do with it, or we get something else, which is probably a more catastrophic failure. Maybe you ran out of memory. There's a no pointer exception somewhere there shouldn't be that point for.

So we'll launch this up. There's a quick little page. It's using the real estate framework. It's been demoed a few times over the conference. And basically there is an agent who's looking through a number of listings, and he's only showing the things he really cares about, which is the listing name, what the asking price is, and how much commission he can expect to get out of that. And if the housing market has gone up, it's In the last few months, you probably want to change all of these all at once, unless something specific. So... We'll take a little look at what this background process is doing. It's made some changes.

[Transcript missing]

From our code, we got the adapter operation, we got what was in the database, we got the What's currently in the database now-- and this is our cached value here-- and then here is the change that we wanted to make. and we had two separate conflicts, so they got involved in the loop.

come back. And then, so this is the last write wins behavior that I was talking about that could be considered a little weird, is that basically everything's been written out, sorry, Everything's been written out according to what this particular editing context viewed the world as. And despite the fact that a number of other rows have been changed to 1,000, and that we were setting inflation in this particular example to 3%, it took the previous value and incremented it 3%, as opposed to taking what was in the database, you know, somebody else changed the value for that listing, and then applying a 3% to that, incrementing that. So, you know, it's pretty application-specific how you want to do that. And, uh... That's pretty much it. I'm just using generic records here, so I haven't done anything useful. I put my UI code here in these component classes, and that's all there is. Okay. Thank you.

Okay, can we switch back to slides? Thanks. So, Ben made that look pretty simple. And once you have it working, I guess it is pretty simple. But for a lot of EOF users and Web Objects users, this kind of problem has troubled them for a long time. So we're going to make that code available in some generic way that would make everything from WWDC available. So you can download that and take a look later. Thanks, Ben.

All right. We just have a few more slides left. I want to talk about raw rows, and this was touched on in the last session. You can certainly fetch raw rows, which are just dictionaries of keys and values, in any of your fetch specifications. The reason for doing that might be because you don't need a full EO or maybe you have a very large data set and you just want some of that information back.

As long as you fetch primary key as part of that raw row or somehow manipulate a dictionary to add a primary key to it, then we can always turn that into a fault or a fault is a shell of an EO. We can turn it back into an EO because you can invoke a fault for a raw row.

And then one trick, you might want to look at our Think Movies example where we have a component that declares a local variable as NSKeyValueCoding. So if you remember, anything that implements key value coding knows how to take values and return values. And typically all your WebObjects components are just using key paths to access values.

So dictionaries will work for that just as well as full-fledged EOs. You just have to make sure that your dictionaries support all the keys that are required in that component. So by using a local variable of NSKeyValueCoding, you can use that component to display both EOs and raw rows.

So when we're talking about EOF, we always say the great feature is you don't have to know SQL. You don't have to write any SQL. EOF will do it all for you. And that's true, and that is a big benefit. It simplifies your interaction with the database. You spend most of your time modeling, and then you can work on your custom web applications without worrying too much about how to talk to the database.

But there are times when you know something about SQL or you want to take advantage of some special feature in SQL, special things that aren't so object-oriented. You might have a complicated SQL expression, or you might have a faster SQL expression than we generate for one of our fetch specifications. In any fetch spec, you can add a hint where you can put your own custom SQL.

All right, so you can find out what SQL we generate and look in your modeler and then decide to change the SQL, do your own thing. So you just have to be careful. Make sure you return the values in the same order that the original fetch specification was going to return values. You can also use custom SQL expressions in any attribute definition.

So if you need access, say, to some SQL function, you don't want to compute it in memory, but you just want to fetch the right value when you're populating your EO, as we mentioned in the previous session, you can set a definition on your attribute. Then for any EO entity, you can specify an external query. Again, you can use your own custom SQL there. EO Utilities has several utility methods. One is called Raw Rows for SQL, and there's also a way to turn those raw rows back into EOs.

EOadapter channel has evaluate expression. So every EOadapter is going to implement some way to evaluate expressions, and you can make direct use of this if you want to send your own SQL to a database. And we'll show you that in just a second in the next demo. And if you want to write your own custom EOQualifier, EOQualifiers live in EO control. So you can define in EO control how-- some new qualifier you want to use in your fetch specifications. You define how it works in memory.

Then you implement another interface that lives in EO access, the EO SQL generation interface for that particular qualifier. You generate the SQL, and we'll use that whenever we're evaluating that query or using that qualifier in a fetch specification. So you can go ahead and augment what we're doing. All right. We'll do another demo. This time it's going to be for me. Can we switch back to demo one? Thanks. All right, we just have a couple of minutes. This demo is not nearly as exciting.

So we have a project here that is just a command line tool for doing a SQL command line. And this is more of something that's kind of cool that you can do it, not that it's something you want to do. You don't, you don't, by any means, you don't need to send your own SQL in your applications, but just to show you that it's all there. So I wanted to show you how we fetch.

Okay, so the code is pretty simple here to fetch. I'll show you an operation in a second. But the basic idea is that once you find an entity, you can ask that entity for fetch specification by the name that was given in this example. Then we can do a fetch specification with qualifier bindings, all right, and I'm holding the bindings in another dictionary.

So that gives me a fetch spec that's ready to execute. Come down here. We're making a new editing context. We're sending that editing context and message objects with fetch specification and the fetch spec that we just built above. And we pulled that fetch spec out of the model. Right, we asked the entity for the fetch spec.

[Transcript missing]

Okay, so the most important part is down here when we're dealing with models. We want to add the model to our model group. We create a new adapter with adapter with model, alright? And from that adapter we can create an adapter context. The adapter context manages the connection and we can create an adapter channel which allows us to send queries. Steve Mineur, Ben Trumbull Okay.

What's the wrong one? You know, I'm trying to find my... So this is the SQL tool running. Make that bigger and I have a couple of queries to show you. So the typical query we've seen in some other sessions here is finding a listing in the real estate database with, say, at least four bedrooms.

Okay. And since I wrote this tool myself, I'm getting back raw rows, and I'm showing you kind of a dictionary representation of the results. With a little more effort, we could show that in a table instead. But this is just to show you I can execute SQL. My SQL tool just read this string, sent it to the adapter channel, and got back some results.

This tool here also has a concept of logging. There's a helpful setting called EOAdapterDebugEnabled that you can turn on to see your application generating SQL. It does more logging of all the SQL calls and the interactions with the database. So I just asked for that to be turned on. And now I have a command line.

Thank you for joining us. I'm going to start with the first part of the session, which is to talk about the model. I think I spelled something wrong. All right, I think I'm gonna run out of time here, so I better take the questions then. I'm sorry, I got the wrong model. But I'll put this code out on the web, too, or wherever we put the WWDC code.

And you can take a look at it, but this is just to show you that you can fetch SQL, and you can use the adapter debug enabled and logging to see what SQL we're generating. And sometimes it helps even debug your own code in that you may have a typo somewhere, like this time. You may have an incorrectly defined attribute, and that causes your SQL to look strange. So, all right, thank you very much. I think we'll go back to the slides, please.

So in summary, well I think you've heard everything. We're running low on time so I'm going to skip the summary and go through the final slides. The beta, the lab, open tomorrow. There's nowhere else to go except for the feedback session so you can bring more questions there. Contact those people, more information. All our documentation, I think you've seen this before.