WebObjects • 52:10
This session demonstrates building EOModels for connecting to databases via JDBC and LDAP servers via JNDI. Learn the basics of creating entities, attributes, and relationships, and explore advanced topics such as custom attribute types and modeling entity inheritance.
Speakers: Matt Firlik, Justin Henzie
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 our next session is Advanced Data Modeling, and I'd like to introduce Matt Firlik and Justin Henzie, who are presenting the next session for you. Anyone know how to work this? Okay, good afternoon. My name is Justin Henzie, and on stage with me is Matthew Firlik. Today we're going to be talking to you about advanced data modeling with EOModeler and connectivity.
Matthew and I both work in the PowerSchool division of Apple using WebObjects. So today we're going to break this session into two parts, which unfortunately for me are not equal. The first part will be using EOModeler to map relational data structures, the concrete example of which is database, into objects. The second part, which will be handled by Matthew, is dealing with connectivity with the JDBC and JNDI adapters.
Okay, so before we go any further, we'll just do a brief resume of EOF. The slide that's on screen at the moment handily condenses every lecture you've been to so far into something you can absorb in two or three seconds. And fortunately for the tone deaf among you, it doesn't require a song, and for those with delicate sensibilities, it does not require me to remove any clothing.
I'll pay you later. Thank you for that laugh. Okay, so what we're trying to display in this screen at the top, you can see a blue cylinder which represents the database. And we're trying to encapsulate that the EOModel helps us take that database representation of the data, convert it first into dictionaries, and then into Java objects which your developers will be familiar with. And what we're trying to convey is that the whole purpose of EOF is to abstract the database away from your developers so they don't have to deal with the nuances of particular databases.
Okay, so what exactly is an EOModel? On the screen to your right, is an example of a very simple model. And I should explain that throughout this talk, we are going to be talking about the features of EOModeler and how to achieve certain functions, and at the same time demonstrating those features. So if you can't see the screen to your right, then it would probably be advantageous to move to the center where you can see it. But they're pretty big screens.
Okay, so a model basically maps a database into Java objects. And a model consists of entities which map individual tables into an object. And an entity has a name, it maps to a table, and it has a class name. Now the default class name is EOGenericRecord, and this is provided for you.
And this basically just provides accesses and mutators to the attributes of that entity. If you want to provide business logic around accessing and mutating those attributes, then you can provide your own subclass or hierarchy of subclasses. where you can implement that business logic. Also in the model is a connection dictionary which dictates the type and location of the database against which you're modeling.
This is a very useful piece of functionality because it actually allows you to browse the data in the database as represented by your model. So it's a very useful tool to check whether your modeling is correct, whether you've actually got the table column names right, etc. And the other high-level artifact that you'll find in the model is stored procedures.
Basically, there are some things that you're going to do which, for efficiency's sake, you might implement as a stored procedure. And what we're trying to achieve is allowing you to expose those stored procedures to your developers through the model so that they basically have one place where they can find all of these things they're used to doing with either JDBC or ODBC.
Okay. So, what then is an EO entity? Well, an EO entity basically, as we've already said, maps a table to a class. And just as with a class, there are certain characteristics that you can specify for an entity. So, for instance, you can declare an entity as abstract.
And as for a Java class, that simply specifies that this entity and classes which you associate with this entity will never be instantiated. They're effectively just generic types. And the reason you would declare an entity as abstract is because perhaps you want it to be the root of an inheritance hierarchy.
So if you have an inheritance hierarchy, obviously you need to be able to specify that the entity has a parent. And in the demonstration now, you can see that our agent entity inherits from our user entity. That basically states that this entity will expose all of the attributes of the user entity.
Now given that you have, I'm going to go forward one slide and then come back. So an example of inheritance in EOF is single table inheritance. And that's exactly what we've modeled here. And you can see that we have four entities in the example which all map to the user table. This is single table inheritance. Essentially every attribute that you are modeling in your entities exists in this table.
The consequence of that is that not every row in that table is appropriate for each entity. So you would want to be able to specify a restricting qualifier to each entity type, filtering the rows in the database so that only the appropriate rows are pulled in that represent that entity. And in this case, you can see we have a qualifier of user type equals one. And if we were to look at the data, you would see that the agent types have a user type of one.
Sometimes entities represent read-only data, reference data, typically read-only. You can specify that the entity is read-only. Now, this is not going to stop you from changing the value of the attributes. It's not akin to the property veto exceptions you would find in traditional Java. But what will happen if you try and save that entity is EOF will raise an exception and abort the save.
Sometimes you know that you are going to want to fetch a certain type of entity repeatedly. And you can actually specify that you would like the entity cached in memory. And this has the effect that the first time you hit that entity, every single row in the database is going to be pulled into memory. Subsequent fetches are going to be against memory, not the database. So you don't have the overhead of going to the database every time you want to fetch.
It's particularly important to understand that caching them does not prevent them from being garbage collected. So if you do have situations where you would like to have objects in memory for the duration of an application, then perhaps the shared editing context is a more appropriate vessel for you. Shared editing context is not part of this lecture. It's actually detailed in Advanced EOF, which is immediately following.
So just as an entity maps a table to an object, attributes map individual columns in those tables to instance variables in your classes. So we have our property entity selected here, and you can see that it has numerous attributes modeled. How do you tell Modeler which attribute to expose in your Java class? Well, we're going to describe the characteristics of the attribute from left to right.
And the first column is these diamond icons, which basically say, "Expose this attribute as a property in my class." Now, you might be asking, "Why would you model an attribute if you're not going to expose it as a class property?" Well, there are certain attributes that EOF needs to be able to perform its functions, one of which is primary key.
Primary key, as in a database, is used to unique that, an instance of this entity in your application. And you can see here we have a primary key indicated by the key icon, and it is indeed not exposed as a class property. Now, you may expose it as a class property if you want, but really changing the value of a primary key isn't terribly useful.
The locking icon. Now, the locking icon is part of EOF's facility to detect optimistic locking failures. And every single attribute that you label as a locked attribute is going to be compared against the database values for that attribute when you try and save. And this is how EOF is going to try and prevent last save wins behavior. Typically, you should not lock any attribute which maps to a large object type, so blobs or clobs. And the reason is, is because EOF has to fetch those values into memory to do the comparison. So there's a memory overhead for doing that.
Ideally, you would like to find an attribute against which to lock, which is guaranteed to change every time you save the database. So, for instance, if you modeled a modified date, which you'd set up your eogeneric record or subclass thereof or editing context to set every time you save an entity, then you could have literally one locking attribute. And this would actually speed up the comparison because obviously the SQL that's going to be generated through the comparison will be simpler.
You'll note actually that the primary key is not a locking attribute simply because it's used to fetch the record anyway. So the next column is allows null. And this basically, as for a database, allows you to specify that this attribute, while mapped to a column in the database, may actually be null. And thus it may be null in the database. Now, from my own experience, I would say that allowing attributes to be null is necessary sometimes. But I would also say that if you can, you should try and get away from allowing your developers to set null.
Finding a database full of nulls is not terribly useful. If you need to specify default values, then within your code, you can actually override methods away from insertion to set default values into those fields. So the next column is the name, and this simply uniquely identifies this attribute within this entity. And this name is going to be used to generate methods for accessing and mutating this property should you choose to expose it as a class property.
Value Class. We need to know what type to map the database type to in your class. We offer a certain, a discrete number of types to map to, and we'll talk about those in just a minute. Column name, obviously the column name that this attribute maps to in the database. External type.
This dictates the type in the database. Now the reason we have to provide this is because most databases have their own types, and they're not always the same for, say, for instance, string. So by allowing us to list the JDBC data types and either restrict or augment those types with a vendor-specific plug-in, we can provide you with a drop-down list of what types you can actually map to for this specific database.
Value types. You'll note in our example that the bathrooms attribute, the value class that we're talking about for mapping to in the Java class is number. And for those of you that are familiar with number, it's an abstract class, so it couldn't actually be directly instantiated. The value type allow you to indicate which concrete subclass of number you would like to be instantiated for this particular attribute. There are actually other uses of this type. For instance, for car database types, you can give a value type which will identify whether you would like to strip or retain training spaces. The value types are all detailed in the release notes, and they're quite comprehensive.
Okay. So you can see the basic characteristics of an attribute here, but there are others. And at the very bottom of the attributes panel, you can see a drop-down which allows you to add additional columns to the view so that you can actually have a customized view. If you want to remove a column, you just select it and delete it, and it goes away.
Okay, so we've basically covered simple attribute characteristics, but attributes can have some advanced characteristics. Notably, if you want finer-grained control over whether your developers can actually write to the attribute rather than marking the entire entity as read-only, you can mark individual attributes as read-only. Custom formatting allows you to specify in a database-independent fashion Processing that you would like to happen to the attribute on a write or a read from the database.
And this processing is going to happen in the database, not in the application server. So a good example is, and one that we use, is truncation of time from a date. So you can simply specify in the write custom formatting that you would like to truncate time off a date.
We're not doing that. As I talked about earlier, we provide a discrete list of classes that you can map to. And nine times out of ten, and maybe 9.9 times out of ten, these are going to be sufficient for your purpose. But on occasion, it's not, and you would like to map it to a class of your own design.
So we allow you to specify a custom class type. And the custom class type, you create the class yourself. But obviously, EOF needs to know how to create that class. And so we need to specify a factory method within that class. And for those of you not familiar with the term factory, all we're really talking about is a static method within the class.
Additionally, we need to know the data type that you're going to be passing to that method. And right now we support NSData, string, and a byte array. So having got your data into your custom class, we obviously now need to know how to get the data back out. And so we ask that you provide a conversion method that will return an appropriate type, usually the same type as the initializing type, that can be mapped successfully to the database.
Sometimes you would like to model attributes in your entities which don't naturally map to a column in the database. And these are called derived types. And typically they are calculated from some other attribute. And you simply change the type from column to derived, enter your formula, and you will have that derived type in that entity. Now because they are calculated from some other value, they are effectively read-only because there is actually nowhere to write them back to.
Okay, so a special type of derived attribute is the flattened attribute. And this effectively is an attribute from an entirely different entity that you would like to expose in this particular entity. So if you have a relationship between one entity, I know we haven't talked about relationships yet, but we will very shortly.
If you have a relationship from one entity to another, you can navigate that relationship, select the attribute you would like to flatten, and flatten it into your entity. And you can see here we have agent last name flattened into property. When you access this attribute, EOF is going to navigate that relationship and pull that value back for you.
Relationships. EOF allows you to map a foreign key from one entity into another, or an attribute from one object to another. Just as you can define foreign key relationships in a database, EOF has the same axiom. Relationships fall into one of two general types. To one, relationships, which basically say there is going to be one instance, potentially one instance of an entity at the end of this relationship. To many, which basically says there's going to be an array, possibly, of instances at the end of this relationship.
Now, we have two examples of relationships on screen. And we're using the diagram view here because it makes it slightly clearer. And we have our agent entity. And it has a 2-1 relationship to agent photo. And you can see it's 2-1 because it's marked with a 1 greater than symbol, and the relationship line has a single arrow. We also have a relationship to properties. Now this is a to-many relationship, which is indicated by the two greater-than symbols and the fact that the relationship line has duplicate arrows.
Having mapped a relationship from one entity to another, it's sometimes useful to make a relationship back to the originating entity. In both cases here, we have relationships going back to the agent. These are called inverse relationships. You should consider carefully what you really want to do with inverse relationships. Typically, an inverse relationship which is a too many may end up firing relationship faults or creating relationship faults for numerous objects which you didn't really intend. Just be careful about what type of inverse relationships you're creating.
[Transcript missing]
So sometimes the relationship between two entities is quite loose. So, for instance, if we look at the property relationship here, we can see that an agent may have maybe a particularly bad agent, and he may manage zero properties. But we may have a particularly good agent on hand, and they may manage many properties. So this relationship is optional. There may be zero or there may be many.
However, we can say that the relationship is mandatory. And what we're saying there is there's a much stronger relationship. And we're saying that for each agent in this case, if we look at the photo relationship, there must be at least one photo. And what's going to happen is if you try and save an agent without a photo, EOF is going to stop you from saving, and you will have to set up that relationship. This sort of relationship won't stop you from fetching the agents from the database, but if you then try and save one which doesn't have this relationship set up, then EOF's going to prevent that save.
Okay, I've missed one at the top here. Batch faulting. This is only appropriate to too many relationships, and when EOF fetches and David Bates The relationships of an entity are populated with faults. In Java, a fault is a fully constituted Java class, but it doesn't have all the data sucked in from the database. So in the case of too many relationships, you have an array of relationship faults. Now as you navigate that relationship and start to use those faults, they fire and become fully constituted classes.
The problem with this is that as you iterate over that array, every time you hit one of those faults, it files a fetch to the database, gets the data, populates the class. So if you have a large array, you can see that it might pose some performance problem for your application because you have many round trips to the database. Batch faulting allows you to say, when I access this relationship, when I fire this fault, I want you to fire that fault and this many more. So you can avoid numerous trips to the database. It's a very useful feature.
Okay, delete rules. Now the delete rules talk about what you want to happen to the destination entity when this source entity is deleted. And the top of the list, and therefore the default, is nullify, which usually isn't what you want. And this basically says that if you have a relationship to a foreign key in your target entity, just null it out. So in our properties example, if you deleted this agent and your rule was nullify, you would null out the agent ID. So this has the implication, of course, that when you're modeling that attribute, you need to mark it as nullable.
And that's one of the very few circumstances, I think, where allowing null is a reasonable thing. So the second rule is cascade. And this basically says when I delete this source, I want to delete any related entity. So in this case, if I were to delete my agent, EOF would attempt to delete the agent photo.
So if you didn't want to delete the photo, you could just unrelate it. Unfortunately, this is mandatory, so I wouldn't let you do that either. So the next rule is deny, and that basically says, if at the end of this relationship there is another entity, then do not allow me to delete this entity. So if you wanted to delete something which had a deny rule, you would have to disassociate the target from the source, and then you could go ahead and delete it.
The other things, owns destination and propagate primary key. These things imply an extremely strong relationship between your entities. Owns destination says that the destination entity cannot exist without the source. So in this case, we have an agent with a photo relationship to agent photo. And the relationship is such that it says that a photo cannot exist without a parent, without an owning agent. Now the implication of this is if you disassociate an agent photo from an agent, then EOF is going to mark that photo for deletion.
The other implication is that if you create an agent, EOF is automatically going to create an agent photo for you, insert it into the editing context, and set up the relationship. Propagates primary key simply says that when that sort of relationship is established, the primary key of the source should be propagated to the destination. So we're basically going to establish a relationship primary key to primary key.
Okay, so having created our model, we've created our entities, we have our attributes, we've set up our relationships. How does it relate to the database? How does it relate to our code? So nine times out of ten, you're going to come to a problem or you're going to be appointed to model this particular circumstance, and the schema probably already exists. There's a DBA that's been working away very hard and naming columns with lots of underbars.
You're going to be expected to map this through to a reasonable Java object model. If you had to do that by hand, it would be an extremely onerous task. EOModeler allows you to reverse engineer an existing schema to create a basic model. In this case, we're going to reverse engineer an open-based database. Obviously, you can reverse engineer as much or as little of that database as you want. So we're going to just go ahead-- you've seen this before, I know.
So there you go. It's created a basic model for you. And sometimes this model might be enough for you. But you're probably going to want to change attribute names. You may want to infer other relationships. But one thing you'll note that's different between our newly created model and our example model is that the inheritance entities that we'd set up previously, notably administrator and agent, aren't included in our new model. So we would want to go ahead and do that sort of modeling ourselves. But it's still a very useful feature.
Now occasionally, and I admit it's very occasionally, you get to a project and you have total control of the schema. So in that circumstance, you can actually use EOModeler to model your entire project and then have Modeler generate the SQL that will create the schema for you. So in our example here, we're going to create the SQL for agent.
Now, again, you can create as little or as much SQL as you like. Now, you can either execute the SQL directly, in which case it will connect to the database you have specified in your connection dictionary, or you can save it off as a separate script so that you can recreate this database whenever you want to.
So now we have our database and we have our model. How do we give our developers what they really need, which is classes? So from an entity, Modeler allows you to generate Java code according to the class name that you have specified for that entity. Now for EOGenericRecord, you don't have to do anything. EOFs are going to create an EOGenericRecord, give it the right entity name, and Bob's your uncle. There you go. Bob Fraser, that is.
But when you specified your own class where you wanted to build up your own business logic, you can generate Java code which can then be imported into your project and your developers can use. So you can save this to wherever you want, import it into your project, put it into your source code system. So we've created one simple class here, the agent. And you'll note in this case that this class does not extend the eogenetic record.
It actually extends user because if you'll remember, agent was one of our inherited classes from user. And basically the generated class is extremely simple. It doesn't contain a lot of bump and cruft. It contains accesses and mutators appropriate to the class properties that you have exposed and the relationships that you have exposed.
So, in the circumstance where you end up with a fastidious DBA creating all manner of tables that you're now expected to model to, it's going to happen. The schema is going to change underneath you, and you're going to be expected to keep in sync with it. Now... A while ago, you would have had to individually, you would look at the database itself, and then you would have to model each individual table yourself.
EOModeler now allows you to synchronize the database schema with your model. And what it's going to do is it's going to examine the schema, reverse engineer it, compare it to your model, and provide you with hints, actions that you need to take to bring your model into sync with the schema. Extremely useful feature. Okay.
So now you have your Java classes, and you have your model, and you have your database. So how do your developers get to those classes? They can't instantiate them directly because there'll just be an empty class. There'll be no data there. So we have these things called EO Fetch Specifications, which are essentially queries to the database which will allow you to fetch a qualified set of objects into an editing context. Once they're in the editing context, your developers can then manipulate them, save them, delete them, whatever.
So we're going to show you a brief example of building a fetch specification. So we're going to build a fetch spec called super agent. And we're going to ask for--you can build the qualifier on any attribute in this entity. So we're going to ask for First name of Tony, because we understand that there's an agent called Tony, and she's particularly good. And by the way, she has no impact on my salary.
So in this case, you can see we used the browse facility to fire that fetch spec and we got the right result back. But obviously, the static search is not really that useful, so we allow you to specify binding variables, which allow you to programmatically specify what your users are actually looking for.
So in this case, we have a binding name of last name. In your code, you would create a dictionary of bindings where the key would be last name and the value would be whatever you're searching for. Sort Orderings. So as well as qualifying the fetch, you can also use sort orderings to sort your results. You can sort on any attribute in that entity, and these sorts can be ascending or descending and can be case sensitive or case insensitive.
Pre-fetching. We mentioned earlier about batch faulting, where you batch fault a particular relationship to make sure that you get a certain number of those relationship faults fired, and thus you have that data to hand with one round trip. Well, pre-fetching allows you to effectively fetch a relationship at the same time as you fetch the entity.
So if you know, for instance, in a particular area of your application that you are going to need to navigate a certain relationship, iterate over all the values that you find there, then pre-fetching will allow you to fetch that relationship at the same time as the entity and will avoid numerous round trips to the database.
Raw Fetch. Okay, so sometimes everybody knows there is a small overhead, small, to creating a Java class. So sometimes there are going to be areas of your applications where it's not efficient to create 10,000 instances of an object when all you really want to do is look at the values in that object and take action from there. So EOF allows you to specify that you want to do a raw fetch. That is, you want to retrieve the entities as dictionaries of values. And you can either fetch all of the attributes or just a specific number of attributes depending on your needs.
I actually would say that doing raw fetches is fine for certain applications, but I wouldn't actually go that way until you actually have realistic performance metrics that say that you really do have a performance problem. In the event that you do do a raw fetch and you then want to leverage the fact that you have business logic in your EOs, there are facilities to turn that raw row into the corresponding Java class.
Options. Fetch limit. Okay, so sometimes your databases are full of many, many, many rows. One million. Thank you very much. So we can restrict how many rows that you can fetch by specifying a fetch limit. And what's going to happen is when EOF fetches, it's going to fetch up to that many rows.
If it hits that fetch limit, it's going to stop. And you can actually specify that you would like to be prompted on that limit. And there are delegate methods, which we don't cover, but delegates are covered in advanced EOF, that you can implement, which will be invoked if that fetch limit is reached.
Sometimes you're going to come across situations where certain users, you know, I'm the boss, I need the info. They're going to say, I can't have a 200 limit fetch, I need every row in the table. So you can either programmatically alter this fetch limit, or alternatively provide an additional fetch specification that doesn't have the restriction. And then in your application code, you can make determination based on privileges or whatever, which fetch specification is going to be invoked.
perform deep inheritance fetch. When you are modeling inheritance in Modeler, as we've already seen, you have root entities and and maybe a potentially quite deep hierarchy down to your leaves. When you fetch that root entity, Setting this option is going to make EOF fetch those entities and all of the subentities as well. So you're going to end up with an array of entities which you don't really know what the types are.
So you would typically iterate over that array and perform instance of operations to determine which actual type to cast it to. An alternative would be to use the abstract type, the user, in our case, and differentiate by values in the table. Fetch distinct rows. This simply uniques the query. So if you end up with duplicate rows in the results of the fetch specification, they'll be eliminated.
They're actually eliminated with a distinct in the query, so it's not something that EUF does when the results come back. Lock all fetched objects. OK, so if you had a one user application and you wanted to strictly control access to the database, then possibly you might want to lock objects. But in the W3 world, the world we all live in, this is actually a very strict restriction of your database. And I would advise you actually don't do it. If you need to lock individual objects, there is API to support that.
So when EOF fetches objects into an editing context, it keeps a snapshot of those objects, and it builds an object graph. If a subsequent fetch goes against the same objects, it's going to use the instances it has in the editing context to service that request. This is a significant problem when you have extremely volatile data. So setting refresh fetched objects means that every time a fetch is fired, it's actually going to go to the database and get the most up-to-date values and bring them back in. So in volatile data situations, this is a good thing.
Require variable bindings simply refers to when we built our fetch specification, we specified two variables. One was static, one was binding. If you set this option, the fetch specification is not going to work unless you specify a value for that binding. If you don't, you can just fire it and we'd end up with results of Tony.
We spoke briefly earlier about single table inheritance, but there are actually two different types of inheritance, two other types of inheritance. The first one is vertical, and basically this specifies that each entity in your inheritance hierarchy is mapped to an individual table. So in our example here, we have vertical inheritance, and you can see that we have a person entity, and we have a professor entity, and a student entity.
And basically, every attribute which is specific to the professor, perhaps beard quotient, would be mapped to a column in that table. The implication of vertical inheritance is this. If you fetch one of those leaves, EOF is going to have to perform as many joins as there are entities in that hierarchy to get the full attribute set to satisfy your request. So with a deep inheritance hierarchy, vertical inheritance can pose some significant performance overheads. The other type of inheritance... horizontal inheritance.
Essentially what we're saying is we have abstract entities in our hierarchy, and our subentities are mapped to tables which specify attributes or columns that match every single attribute in the inheritance hierarchy. In this case you can see our professor and our student are mapped to two individual tables.
Now this has better performance than vertical inheritance, but it does have a maintenance overhead. If you change attributes in that root entity, you're going to have to change the model for both or all of the inherited entities. Okay, I'm spent. So I'll now hand you over to Matthew, who will talk about connectivity.
Because he's English. I'm English. So Justin's talked about two stratified layers of information. We've talked about information from the database-- tables, columns, and rows of information. We've also talked about objects you're generating with attributes and relationships, all sorts of API to manipulate them. These actually correspond to two stratified layers in the Enterprise Object Framework layer.
If you look in the Java doc, if you look in the API, you'll note that there are two packages, one for EO access, which is the lower layer, and one for EO control, which is the upper layer. There's a piece of technology, which is the EO adapter, that allows you to bridge this layer, to take information from one layer to the next and move it back and forth. The EO adapter basically allows EOF to talk back and forth to your database.
Most developers, when you're working with objects, you're working in an editing context. You're working in part of the EO control layer, which has underneath it an object store coordinator. Figures out where the information comes from, how to manage it, how to keep it unique. Underneath that, slightly underneath that, is something called the EO database layer, which has a number of different pieces, a context and a channel, which represents a single connection to a database, a unique connection to a database.
Underneath that layer, though, is a composite layer of adapter information, a context and a channel, in addition to a concrete EO adapter that bridges that gap. Actually, the database layer is the intermediary layer, basically between the editing context, where you want to work with your objects, and then the database, where we need to convert that information to rows, columns, tables, information, and such. And your relational database arrives underneath.
So what we have is we have the JDBC adapter. We say the adapter. In versions of WebObjects Priority 5, we had multiple adapters. We had individual adapters for particular database vendors using their native client libraries. What we have now is a JDBC adapter, which uses a standard API, writing back and forth to JDBC to do all of our database connectivity.
We obviously realize that there are database vendors and specific specifications that need to be altered. Not everyone will play in the same space or play nicely with JDBC. So to alter that, to have vendor-specific behavior, there is a JDBC plug-in to allow us to implement specific features or vendors to alter the specific implementation to suit their needs.
Part of the adapter, as we saw before, was a connection dictionary. Connection dictionary is basically the information that tells the adapter where the database is and how to get to it. There's some required information and there's some optional information. Required information is username and password, which may or may not be used by a database, but most specifically the JDBC URL, which tells the adapter where the database is, protocol, subprotocol, how to connect to it. Optionally, there's a plug-in and driver information that can be specified in the connection dictionary. While we say here that it's optional, it's actually, if not provided, derived from the JDBC URL, which we'll see in just a second.
An example of a JDBC URL in a generic form is JDBC, the main protocol, followed by a subprotocol, followed by the data source. In the example of Oracle, you'll see that the Oracle and Thin is the subprotocol. Afterwards, the data source is recognized by a host name, the port number, and the SID for the particular database. In the case of OpenBase, they just use OpenBase and something that looks like the URL to the database, which is just the machine and instance name for the particular database.
The JDBC plugin, as I said before, customizes the behavior of the adapter, allows us to customize different information based on the particular vendor. If not provided in the connection condition area, it's guessed by looking at the subprotocol, looking at the piece of the subprotocol, basically capitalizing the first letter and adding "plugin" to the end. So in the case of Oracle, you would get capital "O" for Oracle plugin. It is also assumed that if not provided, the package name is as provided, comwebobjectsjdbcadapter. So you need to take that in mind if you are going to work on your own custom adapter.
There are some ways to specify your own plugin information. Obviously, you could put it in the connection dictionary. There's also API, set plugin name for subprotocol, where you can specify a specific one. And lesser known is the fact that you can specify it in Java properties. There are two properties you can set, one which lists all the subprotocol information, and then one which lists for each subprotocol the JDBC plugin to use. This is all in the API documentation for the JDBC adapter. so it's easily referenced.
The driver is also guessed by the JDBC plugin. You can override this in the connection dictionary. As seen here, you'd want to specify the Oracle driver if you needed a special one. It is also assumed that the driver is somewhere within your class path. So if not in a standard location like libExt, you need to specify it in your class path. One quick way to figure this out is when it starts up and you can't do anything, this is probably the case.
In EOModeler, you'll note that there are some options for modifying adapter information. You can either switch the adapter or set information on the adapter. While they seem somewhat the same, there's actually very distinct differences. Switching the adapter has the notion of actually asking you to pick a new adapter, filling out the connection information, and actually after you do that, underneath it's actually going and updating the model.
If the external types have changed, if anything specific to the driver or anything has changed, the model will be updated. If you just set adapter info, all you're doing is altering the connection information. So the model is assumed to have the same database type and the same information that's still specified in the model. So if you are switching database types or rather specific drivers, you want to make sure you switch the adapter, not just set the information.
So new in WebObjects 5 is now a JNDI adapter, which is much like JDBC. It's a generic adapter to talk to a specific kind of information. We're not talking about relational database now. We're talking about Java's naming and directory interface. Standard API, much like JDBC, and there are a number of examples of JNDI systems you can talk to. LDAP, NetInfo, DNS, providing the methods to connect that into your application. Obviously, there are ways to connect to some of these things outside of an adapter context. There are probably much easier ways to get DNS information than creating a JNDI adapter.
But this is very useful when you'd want to model this somewhere in your application. If you have people in LDAP that you want to model into another database or connect into a model of another database or the like. So it provides you with the ability to model this much like you would relational data and provide that into your application.
[Transcript missing]
More than me. Actually, for information on the Relative Distinguished Name, you should look in the API documentation for the LDAP plugin. It's actually spelled out very specifically, but there are some important nuances to it. Unlike with the JDBC adapter, we don't know how to create-- This would be considered a primary key.
We don't know how to create a Relative Distinguished Name for you by default. It could be a composite of information. It's also domain information that's specific to your LDAP system. So there have to be ways to provide this information, to set this as a primary key if you wanted to actually insert a row or create rows in your LDAP system.
One way to do it is to include the attribute as a class property, which I know Justin said we shouldn't do, but in this case, it's actually okay to do. You could create APIs and some programmatic way to set that information. An additional way, though, is to specify, as we said before, a write format.
Instead of specifying SQL as you would with a normal data source, you would specify a format where, in this case, if we wanted our Relative Distinguished Name to be, for example, RCN, our common name, we would just specify CN equals, and then we use a greater than and less than sign notation to specify an attribute within the context of this entity. So in this case, we could specify CN. That would write physically the string CN equals whatever the value of the common name into the Relative Distinguished Name.
There are some differences between object representations in JNDI and JDBC. You'll note that every single entity in this particular model has an attribute called object class. It is not a class property. It's not a primary key. It's a string that represents the object class of an object or an entity within LDAP.
This is kind of like an object within Java, but a little bit different. The inheritance hierarchy is a little bit different. You only actually obtain attributes from the parent in certain cases, and it's important to note. But other than that, everything in the JNDI models works exactly the same as it would in a JDBC model.
That's actually all the information we had on-- specific on the two models, the two types, adapters, and the model that Justin had. If you guys want more information on the WebObjects Beta or be considered for the Beta, there is information on the Appleseed site for accessing that. We still do have the WebObjects Lab for the rest of today and all of tomorrow. If you have questions to be asked on these topics or any other topics about WebObjects.
If you have any questions specific on EOF that we didn't cover or more information on topics that we covered here, you can either ask Justin or I or probably more specifically stay for the session afterwards where they may cover some of these topics in more detail as well as come to the feedback session tomorrow. and the standard list of contact information for people if you have questions outside of what we can answer here. Yeah, it is Bob. And for more information, there's documentation on a number of websites, tech support, as well as mailing lists you can subscribe to for more info.