Enterprise • 1:01:45
This session will provide you with an in-depth exploration of the intermediate and advanced features of the Enterprise Objects Framework (EOF) in WebObjects. Topics include performance optimization, shared editing contexts, raw rows, multi-threaded database access, and data synchronization and locking. This is an intermediate-level to advanced session.
Speaker: James Dempsey
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning. How's everybody doing this morning? That's very exciting. Actually, after that party last night, I'd say that's a good energy level to have. Everybody enjoy the party last night?
[Transcript missing]
Before we get started, let me just do the old poll thing. How many folks in the room are brand new to EOF? Okay, so we got a few folks. Excellent. And how many folks would say you're intermediate? Okay, great. And how many of your advanced folks are just here to make trouble? Oh, yeah! Great.
Excellent! OK. So what we're going to be doing in this session, to get rolling, is we're going to do, for folks who are brand new, a bit of the basics. So you get your mind around a little bit about what EOF is about. And then we're going to cover some advanced topics.
We're also going to do an architecture review, which is helpful for the new folks. But also I find that when you're talking about advanced topics, it's really good to have kind of the picture of what's going on in EOF clearly and freshly in your head. So first, let's do the basics review.
So the Enterprise Objects Framework. What is this thing? So the basic idea, it's an object persistence framework. Largely, the goal here is that you can look at it one of two ways. Either you have this lovely object-oriented WebObjects application, and then you need some place over the network for the information that you've collected from users or want to display to users. You need a spot for that to persist, and a relational database is a very good spot.
Or the other way is usually more often you already have a database, and you want to pull the information out, but you'd like to deal with it in a nice object-oriented manner in a WebObjects application, and EOF fits the bill. So it uses object-relational mapping. Mapping we'll see in a moment from an object model to a relational database model. And the goal, or a goal, is that rather than thinking a lot about what SQL joins am I doing? And what foreign keys? And all this kind of database-specific stuff, to try to use objects that you've gotten from the database in as OO a manner as possible.
So in a nutshell, we're going from a row in the database to an object, and then make some changes to it maybe, and then make sure it gets back in the correct state. That, in a nutshell, is the crux of what EOF is doing. Should be easy, right? All right, so let's talk about some terms.
Entities and attributes. So an entity refers to essentially a class in your object model which will map to a table or view in the database. An attribute is mapping a column in the table to a property or think of it as an instance variable in the object model.
And then relationships in an object model are typically done by reference. In this case, an employee is referring to one department. But of course, when we go to a database model, we're using a join. So a foreign key, in this case in the employee table, is mapping to the primary key of the department. And it's EOF's job to allow you to work with objects largely in this fashion, where you're setting the department of that employee. And then when you save those changes, of course you want it to translate to the proper join in the database.
So what are Enterprise Objects? We talked about what the framework is. Well, you'll hear them referred to all the time as EOs. All that that means largely is that they implement the EO Enterprise Object Interface. Typically, you don't implement that interface yourself. Any of you troublemakers, have you ever implemented the EO Enterprise Object Interface yourself? No, nobody.
Nobody ever has. It comes with the framework. You don't need to do so. Typically, you subclass EO generic record and put your own custom logic in there if you need to. But largely, you can think of an Enterprise Object as just an object like any other, except having all the additional functionality to have built-in persistence.
Now, especially when you first start working with EOF, the first thing you're going to encounter is defining a model to map between that object model and the relational database. And we do that in a tool called EO Modeler, and we'll see a demo of it in a moment. Actually, we will see it in a moment. Where did my demo slide go? Well, regardless, it seems to have disappeared. Let's cut over to demo one, if we could.
All right. We're going to build a model from scratch in a tool called EOModeler. We're going to use an existing database that a few of you might be familiar with. So we connect using JDBC to a wide range of databases. We just need a JDBC URL, which I have. Let me click in the field there.
We're using an open-based database which has some examples, including one that contains some movies. And as we pick that URL, EOModeler takes a peek into the database, asks us a few things, and we're just concerned about assigning primary keys to everything that gets pulled in. And then we get a list of all the tables. We're going to choose Movie as one of the tables.
But as, I don't know, can anybody tell me what a movie might be related to? That's right, a studio. Every movie is related to a studio. So we're going to pull in these two tables. As we hit next and dismiss the wizard, EOModeler has read the schema and reverse engineered the database and given us the beginnings of a model.
So if we take a look first at the model itself, you'll notice that it is mapping a table name to a class name, in this case, a generic record, which is a generic class that can be any type of entity as long as you don't need to add any custom behavior. So we have a movie and a studio. If we take a look at a movie, you'll notice that a movie has various items here. Let's pick one, such as the title.
If we inspect it, you'll notice that this attribute is mapping the name of this attribute to a particular column in the database. In the database, it's a car with a width of 255. Internally, we'll be using a string to represent this item. The same is true if we take a look at, say, a number or a date. It's just a mapping between a column in the database and its data type and a name of an attribute in our object model and its data type.
And then finally, we can take a look at a relationship. Looking at the foreign keys and primary keys, EO Modeler has figured out that a movie is related to a studio. In this case, it has a 2-1 relationship. And if we take a look, you'll notice that it has already chosen a 2-1.
It's related to a studio. And it is mapping the foreign key, the studio ID, To the studio ID of the studio. And once we set that join information up in EO Modeler, At that point, we no longer have to worry about those foreign keys and primary keys, as EOF will take care of that. We'll save that as movies, put it on the desktop.
Now we'll close the model. Actually, let's look at one other thing. The fact that right here from EOModeler, I can make sure that I do have a good connection to the database by browsing the model. And I'm taking a look now at all of the movies that are in our database. Great. Okay. Now, that's interesting, but you're not going to give you a modeler out to everybody who wants to look in your database. You're going to build a web application. So let's go to Xcode. We'll make a small, quick project.
We'll use a display group application since it'll quickly build a user interface for us, and walk through the wizard. We'll call it Demo Woe. We're not going to deploy it in a servlet. We're not going to use web services. But we are going to use JDBC. So that is the adapter we'll choose to connect to the database.
We don't need any additional frameworks, but we do need to choose the model we just created. Pick that model, choose it, and now it'll ask us some questions regarding the first page of this application as we build its user interface. So, the movie entity. Let's get a table and matching records so we can do a search, so we don't get them all at once.
What attributes do we want to display? Let's display a couple. First, the title. Let's do the category, what date it was released, as well as how much revenue it took in. And finally, let's traverse that relationship to the related studio and display the studio's name. What attributes do we want to display? Let's display a couple. First, the title. Let's do the category, what date it was released, as well as how much revenue it took in. And finally, let's traverse that relationship to the related studio and display the studio's name.
There we go. Okay. If we take a peek at this. will bring up the editor. You'll notice that we really have no code going on here. Take a look at the application file. A user session file. It's just a constructor. And then in a WebObjects application, we use components to represent pages. And in the main component, which is the first one that's displayed in a WebObjects application, we do not have any additional code except for some variables to hold a display group, which displays the array of objects, and the movie itself.
Let's open up the component in WebObjects Builder where we define the user interface. And you'll notice that it's created for us a user interface with a search field as well as a table view to display all of the movies that it's found. Let's clean up some of these names. That's the one that bothers me the most because it's got that dot in there. Let's center everything. That wasn't center. There we go.
And let's run this application. So it's building and running the application. And what we're going to see is a web page displayed, and we'll be able to search and display the information from within the database, primarily using the information that was generated and put into that model file. So we can search for movies. Okay, I guess I didn't center that puppy. Let's go back and center it. There we go. And let's look for action movies.
There are all of our action movies. You'll notice that we have the title, the category, the date released using a date formatter, so you can easily format that to whatever display string you wish. Revenue using a number formatter, so you can automatically format that to whatever number format you wish. And then traversing the relationship from a movie table over to studios and getting the related studio name.
And if we type in nothing, we should get all of the movies, of which we have a number. That's an introduction of how you can begin using EOF, primarily starting by generating a model and then building a simple project to get your feet wet with using the model and seeing how it interacts with the database. Thanks. That's it for that demo.
Okay. So now we've got the introduction out of the way. Let's talk a little bit about the architecture of EOF and how it's doing all that stuff. Because that does look like there's a lot of heavy lifting being done behind the scenes that obviously I did not have to do in that demo.
A lot of it does have to do with this configuration. Andreas likes to say that you need to spend quality time with your model. And that is true. Because a lot of what EOF is doing is based on what that model is configured to do, how that mapping is configured, and the details of that model.
So let's take a look at this, what we call the EOF stack, starting with a database, pre-existing quite often, and the model. Now the model you think if it's so important, why isn't it in the middle? Well really the model is configuration information. And it's being used as a reference so the rest of the stack knows what to do. In fact, you could potentially have multiple models, each model referring to a different data source. So you could have one looking up directory information using JNDI and another using JDBC to hit a database. Actually, I have an app that does such a thing.
Since you can have multiple models, there's a class called the Model Group, which we won't talk about much, but its main purpose in life is to manage the fact that there are more than one models potentially. That model group is referred to by the Object Store Coordinator. And as we'll see as we talk, the Object Store Coordinator is, you can refer to it as the root object, or it's kind of the center of an EOF stack.
It's got some stuff going on above it and some stuff going on below it, but it's really doing a lot of the coordination, thus its name. It's also kind of the choke point for doing things like multi-threading and concurrent access. Essentially, that Object Store Coordinator is the centerpiece of an EOF stack.
Now going up the stack, because this is the object that as in using EOF you will most often interact with, is the editing context. And the editing context is the go-to object. If EOF were a movie, the editing context would be the leading man or woman. I don't know which. We've never established the gender of the editing context. Andreas, any? No preference.
So the editing context is your go-to. You ask it to fetch. It's the object that you, when you create a new enterprise object, you insert it into the editing context so it'll be ready to get inserted in the database the next time you tell the editing context to save. When you delete an object, you have to tell the editing context.
When you have a bunch of changes and you want to revert them, you tell the editing context. And whenever you change an enterprise object, the editing context is observing, and so it notices those changes, keeps track of that fact so it knows to save those changes to the database. So the editing context is really your go-to object in EOF.
Now when you do ask an editing context to fetch, and that is essentially what we did when we typed in action and hit the submit button, the editing context was asked, "Hey, go get things that have action as their category." Coming down the stack, the object store coordinator finds out there's a fetch going on. On the fly, it actually creates these next objects if they weren't there already. We'll talk about what they do in a moment. Then the adapter is what actually talks to the database. So let's say that fetch category we want action heads all the way down.
The JDBC adapter is going to generate the SQL that actually gets sent to the database, and what it's going to get back are called raw rows. They're not fancy. They're not enterprise objects. They're just dictionaries full of key-value pairs. There's nothing particularly special about them. They're just raw data containers.
They also don't take advantage of a lot of the nice features and functionality of EOF. That happens at the next layer up. Those raw rows are handed from the adapter layer up to the database layer where a raw row is turned into an EO. It's kind of like Pinocchio becoming a real boy. That's essentially what it is.
Instead of a wooden raw row, you get a full-fledged object with an object graph and the like. At that time, a very specific thing happens. A snapshot is taken of that row. That snapshot, as we'll see in a moment, is maintained. It's a very simple way to do that. We'll see that snapshot is very important when we talk about data freshness, which we will do today.
So that snapshot occurs, and at the same time, that raw row is turned into an EO, an enterprise object of the correct class that you specified in the model is created. The data is populated. Any related-- well, we'll get to that in a moment, because there's the EO.
And finally, besides that EO, any relationships, a little object is created called a fault. It's a little placeholder that says, well, you don't necessarily need the studio right now, or you don't necessarily need the list of every time I've logged in that's a related to many array right now. Just put a little placeholder there. When you ask for it, I'll go get it then. Okay. I'm going to go forward, but I forget. That might take me too far. Yes, it did. Oh, no. I've got to build it again. Okay.
Quick review. As they go by, remember what I said. Everybody with me? Excellent. So looking at this, when we talk about advanced topics, it's good to have this picture in your head. It can be, you know, you can use circles instead of boxes in your head if you want. It's your head. But the basic structure should remain the same.
And the things to think about are the things that you often need to think about are what's going on, that relationship between the EO and its snapshot, what's going on with these faults, when are they getting triggered. Those tend to be very important control points when talking about things like memory management or data freshness of your things that are in your EOF stack. Now let's talk about a couple of common configurations of EOF. The first of which is multiple editing contexts.
And so in addition to one editing context on top of this stack, we can have many. And in fact, this is the default configuration of a WebObjects application. Every user that shows up to the site gets a new session. Every session has a new default editing context which sits on top of the same object store coordinator. What that means is that every session has a new default editing context. So what that means is that they all share the same snapshot.
And so this is where some of that caching comes into play, where just because somebody just showed up to your site, doesn't necessarily mean that we have to fetch everything once again for them, because a snapshot from somebody else's session might have already grabbed that information and kept it in memory. And so we can have that same row in the database, have a snapshot for it, and have... I'm going to... Yeah, there we go.
I'm having some clicker problems. Okay. So in this case, these EOs could both be the same row underneath in the database, but they're in different editing contexts, which means that one user can make some edits to it and then decide not to save those edits. The other could do the same, but underneath both of these EOs is a snapshot.
I'm having some clicker problems. Okay. So in this case, these EOs could both be the same row underneath in the database, but they're in different editing contexts, which means And we'll add two stacks. Now, you say, well, wow, multiple stacks. That sounds kind of out there. But really, as soon as you launch a second instance of your WebObjects application, you have a situation where you have multiple stacks.
One app has one stack. One app has the other stack. And as we'll see here, they can both deal with the same row from the database, but they have a different set of snapshots. One stack does not know about the other stack. And what essentially defines creating a stack is that object store coordinator, that middle center piece. Okay.
That said, everybody, well, actually, let's go back one. Everybody have that clear in your mind. So now we're going to talk about memory management and then a little bit about data freshness. So memory management. As I looked at this material, I realized that I was using EOF circa 4.5 in my head rather than 5.2.3.
So, things to note: the editing context is using weak references, so only things that it actually needs to hold onto, things that are pending inserts, deletes, or edits, or updates, need to be retained. But the EO itself has a strong reference back to its editing context. This has a couple of caveats.
The first is that if you ask for all the registered objects in an EO, it's not particularly useful as some of them may have been garbage collected. You can, if you want the old behavior, set retains registered objects, which will do that on the editing context. The editing context subclass, shared editing context, always does use strong references, I should say.
So faulting. I talked a little bit before about that kind of ghost object called a fault. What that is, in essence, is an empty shell of a particular type of object. So as this says, the size of the fault is the size of the EO. It's essentially created using the constructor, but not initialized with values to defer the fetching of that value until you need to access the object.
But what this means is that when we fetched in that original movie, there was a fault of a studio kind of hanging out there, taking up as much size as a studio. Deferred faults allows a setting that essentially allows that creation of the fault even to be deferred, so that there's this single shared object that's standing in for the fault, which is a stand-in for the real object, that is more memory efficient.
So if you are extending EO generic record, you'll automatically have deferred faulting. Otherwise, you will want to-- if you're using EO custom object, you will want to override a method which seems to have disappeared from the slide. I think something like uses deferred faulting. You want to return true to that.
The reason why Awake from Fetch and Awake from Insertion is up here is that those two methods, essentially if you look at the process of creating a fault, a constructor is called. If you put your initialization logic in that constructor, well, when that object is finally initialized, it's going to pull information up from perhaps a snapshot or the database, and all the work you've done might get overridden. The appropriate place to put initialization information is by subclassing generic record and then putting it in Awake from Fetch or Awake from Insertion.
Some other notes, some good tips, a blob of data. You may want to factor that out into its own table. I think the canonical example is an employee and their employee photo. If you have some little app and you're always just searching for first names and email addresses and phone numbers, you don't want to have to bring their whole image in because it's part of that row. A better thing to do is to have a 2-1 relationship to another table that stores the blob of data so that you only have to bring in the image.
You only have to access it over a relationship when you need it. Some blobs might be better just to store them in the file system and store a path to the blob in your database. Some other optimizations. Sometimes you never use a too-many class property, so maybe you know that the employee belongs to a department, but in your particular app, your department never needs a list of all the employees.
By turning off that reverse too-many, it can save that fault from being created and save you some memory. Also, some rarely used attributes, like you might have that employee where you're always looking up first name, last name, email, but then they have all this other chunk of data, like their social security number and all kinds of crazy stuff. You might have just the employee and then a join to a table that has a lot more in the other table, but that's used more rarely.
Another thing is you can actually hard code a fetch that will do the too-many relationship for you so that it's not doing a join, you're just using a fetch to grab the objects that you need. If you're using key-value coding, the name of that method, you can bind that up as if it's a relationship. Key-value coding won't know how that array was provided, it just knows there's a method name that it finds and calls to return that array.
The shared editing context. So the purpose of the shared editing context is, if we go back to that picture of the stack in our head, is that I mentioned that multiple editing contexts can all deal with the same exact row as a separate EO in each editing context.
So there's memory used for each EO in each row, excuse me, in each editing context. A shared editing context allows you to fetch things into this shared editing context that conceptually those EOs are part of every other editing context sitting on that EOF stack. And so the memory is shared not only for the snapshots, but also for the EO. There are some caveats to using this.
The first is that with that shared editing context, you cannot have any relationships that go out of the EOs in that. You can have things that point in, but nothing going out. Otherwise, you will hit trouble. Let's see. And you can't use it across stacks, right? It's sharing the same snapshot, and it's only dealing with items that are on the same EOF stack, which means the same object store coordinator.
Now what if you got tons and tons and tons and tons and tons and tons and tons and tons and tons of EOs? Well, we've got a million EOs batching, right? You want to batch things up. There are a few things that you can do. One is you could use raw rows because when you're using raw rows, it's quicker. You're not generating snapshots or EOs. You're just getting those raw dictionaries back, which you can use to display. Actually, they're probably much more handy for display purposes than for edit purposes.
Project Wonder also has some nice reusable stuff, such as a fetch specification batch iterator, which will let you fetch things in by batch, and also a way to get an object count with a particular qualifier. It's easy for you to build that user interface that says, now showing 1 through 10 of 7.2 million.
Has anybody here ever... gone through 1 through 10 all the way up to a million on a website. You have? That's a lot of time you have on your hands. Because most users won't go through a million records on a website, so often you want to give the impression that you grabbed them all, but not do so.
And then sort of the lowest level you can possibly get is you can actually go down to the very JDBC adapter, the JDBC channel, and override fetchRow, which is the kind of atomic method that's fetching in every single row from the result set. You can override that method, call the super so the fetching happens for you, and then you do something with the data and return null for the method.
So EOF thinks nothing came back for that row, yet you've already grabbed the information, done something like aggregated it or something of that nature, and you get back all these results, and the rest of EOF thinks you got nothing back. It's kind of tricky, but it definitely works.
Okay, that's some memory management stuff. Now let's talk about data freshness. So data freshness, EOF is a big, big cache. So those snapshots are caching. And any time you have caching, no matter the environment, you have a data freshness So, there are a lot of techniques and features within EOF that deal with data freshness and keeping your data fresh. What I'm going to do now is rather than go through them in slides, let's go to a demo. Let's go to the demo machine. Let's get rid of this demo. Boom.
So one thing I've found is that although I have been through what refaulting is, refreshing is, invalidating, all of these various things that deal with EOs, That over time as I'm developing with EOF, their definitions get a little fuzzy in my head, especially when I have an app that's running and working, and then I have to rethink through everything. Okay, now, this one's getting rid of the snapshot.
What's this doing? What I did was I wrote a little app that I call the Freshness Explorer. It's a WebObjects app that I think helps to illustrate, much more than slides can, what's going on with the EOF stack. Great for this to pop on up. Can't find server.
Let's see if we do the old -- what was it? Oh, okay. Let's hit it once again. Great. Okay. So this I call the Freshness Explorer. It's essentially what we're seeing here are two EOF stacks, each one sitting on top of an object store coordinator. Then within each stack is an editing context, or two editing contexts, both sitting on top of the same one, displaying some pertinent information about the editing context that we'll chat about in a moment.
The related snapshot, if there is any. Any SQL that that stack had sent to the database in the last transaction. And then finally, doing a raw row fetch to get the actual database row that we'll be talking about. Let's fetch into this editing context, as well as this editing context. And so the first thing to talk about is the idea of refaulting.
So we have fetched these objects in. You'll notice that we have two EOs, one in each editing context. Here's the underlying snapshot, which is holding onto this information. And since we did a fetch, there's a SQL transaction that occurred, and here's the database underneath. We can do some things to this EO. The first thing we'll do is something called refaulting.
And the behavior when you refault, as you'll see, is... That item is turned into a fault. It's a fault. The snapshot hangs around, and nothing else is affected in any other editing context. If we touch the fault, No SQL is generated. The fault notices that there's a snapshot and says, "Okay, I'll use that information." Now, if I have a pending edit with a fault, so instead of Bob Jones, let's say we're going to Robert Jones, I'll submit but not save that change. So that change is hanging out in that Enterprise Object in the editing context.
Now if I refault, It's turned into a fault. I touch the fault. Notice that refaulting loses all of my pending edits. So that is the behavior of refaulting. You get that behavior by telling an editing context to refault an object and handing it the object you wish to refault. A smarter version of refaulting is called refreshing. So let's edit Robert once again.
And by refreshing, actually, let's do two things. Yeah, let's do that. Now if I hit refresh, A fault was created and then basically refreshed from the snapshot with the changes applied on top of it. The granddaddy of all data freshness things, the sledgehammer is invalidate. Okay. Somebody likes the sledgehammer analogy.
So when you invalidate, all sorts of things happen. This object is turned into a fault. The snapshot is discarded. All other editing contexts that have that row within it also get turned into a fault, and all pending edits are discarded. So it's a very, very draconian way to refresh things. Let's fetch things back.
Here's one that I wasn't expecting, which is when I refault, the snapshot hangs around and the thing turns into a fault. But notice if I refault with one EO and one editing context, my snapshot disappears. This is due to something called snapshot reference counting, which happens automatically. When no EO is referring to that snapshot, it goes away. Now, I knew that in my head, and I had always thought of that as a memory management sort of thing. Okay, we don't need it.
We can get rid of that memory. But as I was building that, that interaction was not apparent to me until I was playing with this app. So, that's kind of cool. So let's fetch some things in and talk about a couple of other items. Let's go to this other stack.
You'll notice that if I make a change over here, like Robert Jones, Submit that. When I save the change, I should have just submitted it. No SQL got sent. The database is the same. Let's save the change. You'll notice that what happens when I save that change is the snapshot gets updated, some SQL gets sent, so the database row is updated, and in addition, The other EO is turned into a fault. As soon as it gets touched, it has the latest values.
So now we have a data freshness conundrum. One stack has made a change, and so Robert Jones is here, but we still have Bob Jones. And as long as I refault and touch the fault and refault and touch the fault and refresh and touch the fault, I'm not getting Robert Jones. So I'd fetch. Well, if I fetch, I still don't have Robert Jones.
When you fetch, EOF is, as I said before, a big old cache. And it is an aggressive big old cache. So when you fetch, the main reason it's hitting the database is not because it's concerned about the stuff you already have. It's concerned because there might be rows in there that meet the fetch criteria that you haven't already grabbed.
So anything you already have, by default, if you have a snapshot, it'll say, "Okay, I got that. I don't have to look at it." The key toggle there is on that fetch specification to refresh the refetched objects. So check that off. And now we'll get Robert coming in. Since the snapshot has changed, any other EOs in other editing contexts are turned into a fault. So we'll touch the fault.
Often using a fetch with refreshing refetched objects is the most direct and efficient way to refetch Fresh, a large amount of items at the same time, especially if it's a well-known group of data or set of data that you can fit into a qualifier. All right, so everybody's now Robert Jones. Yes, yes. Now let's make the EOF stack number two. Let's say it's Robert Cray.
[Transcript missing]
can be a source of confusion sometimes. Has anybody found it to be a source of confusion at times? Some nodding heads. What's going on with the fetch timestamp is this. When you create an editing context, By default, a timestamp is put on it that's an hour previous to when it was created.
So we showed up here and started running this at about 9:15. Excuse me, at about 11:15. And so the timestamp on all of these editing contexts is at about 10:15, an hour before this thing was created. And that will never, ever, ever, ever change on that editing context unless you change it yourself. What this means is that any time that editing context looks at a snapshot, it's going to say, or 10:15, if it's later than 10:15, it's fine with me. It's fresh enough.
So you'll notice that here we have a fetch timestamp. Every time a snapshot is created, it gets a timestamp. In this case, 11:19:08. So no matter what we do, without changing the editing context timestamp, things that are fetched in by that editing context are going to have a timestamp later than its timestamp. Which means that if I refault, Touch the fault, refault, touch the fault. I keep using that snapshot. One mechanism for data freshness is to update the fetch timestamp in the editing context. In this case, we'll update it to now.
Now it's 11:24. Now 11:24 is later than 11:19. So now if I refault, when I touch that fault, It's going to notice 11.24 is later than 11.19. It will hit the database, pull back the freshest values, make a new snapshot, and there you go. Okay, is there any other items that I wanted to chat about here? That's about it. Let's go back to slides.
So I intend to have that available. I find it -- oh, cool. Just in building it and playing with it, I know I learned some things. The other thing is that I think that often we get, I'm refaulting or I'm doing this one thing, and we think about it in isolation.
What I like about playing with that, because that's essentially what we're doing is playing with it, is that you see interactions between these different techniques that you may not have noticed or thought about before, but they're very obvious to you when you see them graphically. I think I'll be using that a lot now that I've built it. Great. Data freshness. Now, since... Oh, there's my slide.
So since we don't have kind of a way for that demo to get to everybody else who might not be seeing this session live, we're going to have some slides so they'll see it. But I'm going to crank through these because we've already talked about most of this stuff in the demo. So refaulting, again, affects that one editing context.
We saw that it turned into a fault. We touched the fault. It uses the snapshot if it is fresh enough. It doesn't affect any other editing contexts. And there's no database round trip unless for some reason, for example, that snapshot isn't fresh enough. We can't use that existing snapshot.
Invalidating is that-- Sledgehammer approach. All editing contexts lose all pending edits. The snapshot's thrown out, and then the database is hit. Now, invalidating is pretty heavy-handed. There are some things you can do. You can imagine we had one object. So, okay, I invalidate, and then I touch the fault, and then I get one fetch to the database. But imagine, you know, you're displaying that million records.
Well, let's say a couple thousand records that you have, and you say, invalidate all objects. Now, every time you touch one of those objects, it's a separate transaction, a separate fetch to the database by default. So, get that one. You have some repetition you're displaying on a page, and it's like, hit the database. Oh, I need another one. Hit the database.
Oh, I need another one. Hit the database. Oh. And it does that thousands of times, which is obviously a performance hit. One thing that you can do to prevent that is to set batch faulting, which is set in that very important model file. And the idea behind batch faulting is you set a batch number, and it's sort of like when you're at home and somebody else is up at the refrigerator while you're watching TV.
And you say, while you're up... Why don't you get me something too? So batch faulting essentially saying, while you're going to the database to get one studio, you might as well bring back 10 or 100 or whatever number as you go through this seems to make sense for your data set and the size of your data. That will definitely increase the performance.
Manually fetching to restore snapshots using refreshes, refetched objects is potentially even better because you're not relying on the infrastructure. You're specifically saying, I want these particular objects that match this particular criteria to get fetched at this particular moment in time, returned to me and refreshed. So that's very precise control.
And while you're doing that, you can use prefetching. Which are key paths that you specify along with the fetch that say, while you're down there getting all these, in this case, movies, bring back the related studio. And so you will get not only a fresh version of all the movies, but a fresh version of all the related studios, all within the same database transaction.
Refresh Objects. We hit on that briefly. It's like Refault Object, except it is not going to throw away pending updates or inserts, and it will use that existing row snapshot. Playing with this, the behavior is that if there are no changes to the object that you refresh, it actually turns it into a fault. If there are pending changes within the request-response loop, it seems to turn it into a fault, unfault it, and apply the changes.
Data freshness. This is one that, again, I think is much easier to see the results of it than to explain the results of it. But again, you make a new editing context, it gets a timestamp of an hour ago. So you show up at 10:00 AM, a user does, it gets a 9:00 AM timestamp.
It's an absolute time, and unless you do something, that timestamp never changes. Anytime a snapshot is created, it gets a timestamp. Those two are compared. If the snapshot is too old, then that snapshot will not be used the next time that editing context is trying to use it.
What you might do, and what I do in the source code, is I just tell that editing context to set its timestamp to something else. You know, make a new NS timestamp. You can do that perhaps when the session wakes up, so that every time that user has a new transaction, you update the timestamp. And that then turns it into more of a rolling window, so that every time a transaction occurs, and then some faulting occurs, you can make sure that it updates. And then again, the set refreshes refetched objects, and the prefetching.
Now, if you want to go just crazy, you can go to the EO database, which was on our model. It's the object that's actually maintaining all of those snapshots, and you can mess with them directly. So an EO Global ID for you new folks, it's simply an object that is a unique identifier for an enterprise object. And it's how you basically map up what snapshots go with what EOs. For database folks, it's basically the name of the entity and the primary key information. Shh, don't tell anybody I told you.
Cheating at faulting. Essentially, you can record your own snapshots. You get the global ID and you say, here's the snapshot for that particular object. So if you happen to have gotten it from some raw row fetching or some other means, you can just provide it, potentially saving a fetch to the database. Pre-flighting too many relationships. So sometimes you have a new enterprise object and, you know, a department that's brand new does not have any employees.
Yet, EOF doesn't know that necessarily there are no employees in the database. So if you go to touch that relationship, it is going to automatically do a fetch to look for employees. You know that there aren't any because you just made the department. So you can use this method, record snapshot for source global ID, to hand it an empty array so that it thinks that it has, well, it will have a snapshot that says there's nothing related to it. Therefore, it's not going to hit the database if you access the too many arrays of that newly inserted object.
And if you want to just blow away the snapshot for a too many so that the next time it's accessed, it is absolutely positively refreshed, you can use that same method and hand in null instead of an array, which will get rid of that too many snapshot. And so the next time that object with that GID and that relationship name is accessed, it'll say, I don't have a snapshot. Better go get fresh information from the database. Again, this is very low level stuff.
More likely you would want to use, say, a fetch specification with prefetching necessarily before having to revert to this. And then when you're doing this stuff at the EO database level, we'll talk about multi-threading in a moment, you want to lock and unlock the object store coordinator. That multi-threading.
I can't go through all of concurrent programming right now. We have only six minutes left. So there are a lot of good books. As we talk, I'm assuming that you have some knowledge of concurrent programming in Java and concurrent programming in general. There's some good docs on this in the What's New in WebObjects 5.2 docs.
Java apps, always multi-threaded. Here are some examples of multi-threading. Finalization, timers, session timeouts, if you do your own threads, notifications. So you cannot, it is impossible to write an EOF application in WebObjects. So you cannot write an EOF application that is completely unmulti-threaded, which means we need to deal with it. Use try finally. So try to do something like locking a resource. We'll tell you in a moment what you need to lock. Then in the finally block, unlock it. So if any problems occur, that finally block is always called and you don't deadlock your application.
Essentially, if an object in EOF or WebObjects Foundation does not have a lock on it, you need to protect it. Either with a synchronized block or using your own lock or some way of doing it. So that's arrays and dictionaries. You need to lock those or deal with the concurrent issues yourself. For things like editing contexts, the object store coordinator, you can lock those. It has the API to do so.
You can also use try lock on an NS recurring lock or an editing context so that you won't block as you see is this thing able to lock for me or does somebody else have the lock. As you're debugging, you can use control slash or kill quit with the PID to produce a stack trace by thread as to what's going on.
The commercial products optimize it in JProbe are also very, very handy. As you're using an editing context, if you're using a nested editing context, you need to lock the nested editing context. Even though it is using its parent's lock, you still do need to lock that child editing context.
The shared editing context locks itself. So it's the one exception that proves the rule. That's the one that locks itself. Everything else you need to do yourself. You need to always lock the editing context whenever you're using any of its Enterprise Objects in your own code. So even if you're doing some read-only stuff, you still need to lock the EO--excuse me, lock the editing context, do whatever stuff you're doing in code, and then unlock the editing context.
If you're operating at a lower level than your EOs in editing context, you'll want to lock the object store coordinator instead, as we talked about with EO database stuff. So that is showing us that that object store coordinator is, in essence, that root object. It is the thing that, around which all this locking is based, is that object store coordinator.
So remaining things to be careful of as you're bopping around EOF, locking around the model, notification center, and entity. In WebObjects, they have a method that allows concurrent request handling, which has nothing whatsoever to do with EOF multi-threading. It has to do with whether WebObjects applications are handling incoming requests in a multi-threaded manner. That doesn't have anything to do with whether you've turned on multi-threading in EOF. As we've just said, it's always to some degree multi-threaded.
So, some things to watch out for. Intersecting locks where you need to have this lock and this lock to be able to do something and then release this lock. There's more possibility for deadlock. Keeping things as dead, awful simple as possible to solve the problem is the best bet.
When you synchronize some methods, they're not just fancy keywords, there's an object lock going on there. And so when you're in a synchronized block and then you lock something else or do a blocking operation, nothing else is able to get into the synchronized block of that object, any synchronized method of that object, until that code is released. So, try to keep your blocking code locked by out of the synchronized blocks, but possibly using wait and notify or recursive locks. Concurrent database access.
So, this is pretty fun. Concurrent database access, the general idea is you have multiple EOF stacks. Each stack, again, with an object store coordinator of its own, is able to have concurrent access to the database. So you want two things hitting the database, make two stacks. That's the takeaway, basically. Each of those stacks has its own set of locks, database channels, snapshot cache.
Since you do potentially have the overhead of multiple snapshots for each of those, this is especially handy if you're fetching raw rows, because there's no overhead with snapshots or EOs or notifications going on. You're just getting back an array of dictionaries that you can do with what you will, which can be very handy if potentially you're doing an app where you're doing a lot of fetching in and displaying of stuff, potentially with a little less editing going on.
To make one is dirt simple. You make an object store coordinator that takes no arguments. At that point, you've just made the basis of a new EOF stack. Then make a new editing context and hand in that object store coordinator as the argument to the constructor, and then start using that editing context.
All of the objects beneath the scenes that are doing the SQL generation that I'd like are created for you automatically the first time you touch the database. I mentioned raw rows already. And the actual concurrency, it should be noted, depends on how you have your database configured. Sometimes it's a configuration issue with the database.
Sometimes it's a money issue with the database where you can only have so many connections because of your license. But oftentimes the reason why you might not have a database is because you don't have a database. The reason why you might not be seeing concurrency is that you haven't configured the database right or you haven't paid the database company enough money.
Okay, so we've reviewed some basics, some memory management, data freshness, multi-threading, and concurrent database access. For more info, there's documentation up in the reference library. There's who to contact. Andreas, Bob, Catherine Wentz. And enterprise-level WebObjects support and consulting. I don't see anybody jotting down, so I'll go on. The reference library. And now let's have some folks up for Q&A. Thank you.