| <?xml version="1.0"?> |
| |
| <document> |
| <properties> |
| <title>Peers Howto</title> |
| <author email="leon@opticode.co.za">Leon Messerschmidt</author> |
| <author email="jvanzyl@periapt.com">Turbine Documentation Team</author> |
| </properties> |
| |
| <body> |
| |
| <section name="Working With Peers"> |
| |
| <p> |
| Peers are an Object Relation mapping tool, built on top of Village that gives |
| you access to a relational database via Java objects. Peers act on a somewhat |
| lower level than other O-R mapping tools like Castor or Osage. This means that |
| you have to do some coding by hand, but it allows for the most flexible |
| solution. |
| </p> |
| |
| <p> |
| Peers use Turbine's Database adaptor classes that make uniform connection |
| to a wide range of databases possible. If your database is not supported |
| you can read the <a href="../db-adapters.html">Database Adapter</a> docs on |
| how to create a new adaptor for your database. |
| </p> |
| |
| <p> |
| NOTE: If you would like to use Peers outside of the Turbine Servlet |
| (this is very common), then you need to use the TurbineConfig object to |
| initialize Turbine's code with the right TurbineResources.properties |
| file <strong>before</strong> you execute your Peer code. Please see the |
| javadoc for the TurbineConfig object for more details. |
| </p> |
| |
| </section> |
| |
| <section name="Capabilities"> |
| |
| <source><![CDATA[ |
| > I'm looking for an O-R layer for the next version of our system; |
| > somebody recommended that I evaluate Peers. I've identified 7 |
| > structural cases and a few other criteria that I'd like in an O-R tool. |
| > Can an experienced Peers user or developer tell me how well it handles |
| > these situations? |
| > |
| > O-R structures: |
| > 1) simple (1 class, 1 table) |
| |
| You use a tool called Torque to generate Peer classes for you. There |
| are 4 classes for each table. Base<table-name>Peer, Base<table-name>, |
| <table-name>Peer and Base<table-name> |
| |
| The Base* classes contains all the functionality and should not be |
| change. The other two classes are empty and this is where your |
| application business logic goes. If you regenerate with torque only the |
| Base* classes changes. This allows you to change the schema, but still |
| keep your existing code. |
| |
| > 2) 1:n |
| |
| Torque will generate methods for you to access the relevant objects. |
| |
| For example Category and Item. Category.getItems() will give all the |
| items for a category or Item.getCategory() will give the associated |
| Category for an Item. |
| |
| Torque also generates methods for joining all objects with less db hits |
| as to improve performance. |
| |
| > 3) n:m |
| |
| You can use the methods generated by Torque for this, but it would |
| probably be more efficient to use a Criteria object. Joins are very |
| easy to do and you'll find that you'll be able to do complex multi-table |
| joins without a problem. |
| |
| > 4) self join 1:n (object trees) |
| |
| Same as (3). I have very efficient code that loads a tree from a table |
| into a in memory tree representation with a single db hit. If you're |
| interested I can give it to you. |
| |
| > 5) self join n:m (object maps) |
| |
| Same as (3). |
| |
| > 6) simple inheritance (S extends B, each maps to a table with a shared |
| > primary key) |
| |
| I don't think there is any support for this at the moment, but it could |
| probably be done. |
| |
| > 7) polymorphic inheritance (S and T extend B, the application works with |
| > a collection of B) |
| |
| Same as (6) |
| |
| > The only other real requirement I've got is good documentation. |
| |
| As an added bonus Peer allows you to create objects from a standard SQL |
| query. This gives you the opportunity to do things by hand wherever you |
| might find Peers lacking (which isn't a lot :-) |
| |
| The documentation is coming along nicely, but there is room for |
| improvement. We'll help you with Peer if you help out with docs :-) |
| ]]></source> |
| |
| </section> |
| |
| <section name="Database Maps"> |
| |
| <p> |
| Peers make use of a DatabaseMap class that holds internal data about the |
| relational schema. You will seldom, if ever, need to work with the |
| DatabaseMap class. It is used internally by Peers to discover information |
| about the database at runtime. |
| </p> |
| |
| <p> |
| There is exactly one DatabaseMap for each relational database that you |
| connect to. You may wish to connect to more than one database in your |
| application. You should then have one DatabaseMap for each of the |
| databases. |
| </p> |
| |
| <p> |
| DatabaseMaps are constructed by classes called MapBuilders. Turbine has |
| a default MapBuilder, called TurbineMapBuilder, that creates a |
| DatabaseMap for Turbine's internal database structure. Again you should |
| seldom work with MapBuilder classes. They are used internally by Peers. |
| Usually there is a MapBuilder for each table in your database. |
| MapBuilder classes for new schemas can be generated for you automatically. |
| </p> |
| </section> |
| |
| <section name="Peer Classes"> |
| |
| <p> |
| Everything in Peers resolve around Peer classes. A Peer class has a |
| one-to-one mapping to a Database table. You use each table's associated |
| Peer class to do operations on that table. Peer classes can be generated |
| for you automatically. |
| </p> |
| |
| <p> |
| Peer classes have static methods only, so you would never create objects of |
| Peer classes. It is not necessary to have objects on this level because |
| of the one-to-one mapping with a table. Peer methods are thread safe. |
| </p> |
| |
| </section> |
| |
| <section name="Data Objects"> |
| <p> |
| A Data Object holds information about a single row of a specific table. |
| Data Objects can be generated automatically for you. It takes the form |
| of Bean properties for each field of the table. |
| </p> |
| |
| <p> |
| Data Objects are used almost exclusively with their related Peer classes. |
| Where peer classes "wrap around" around a database table, a Data Object |
| "wrap around" individual rows of the table. The two always go together. |
| </p> |
| |
| <p> |
| You normally use Data Objects in one of two ways. The most common way |
| is to extract data after you called a doSelect on a Peer class. The |
| doSelect method returns a vector of Data Objects that holds the data of |
| the resultset. Secondly you can create Data Objects and pass it to the |
| overloaded doInsert and doUpdate methods of the relevant Peer class. |
| </p> |
| </section> |
| |
| <section name="Criteria Objects"> |
| |
| <p> |
| Criteria is an abstraction of the criteria of an sql query. We use |
| criteria objects to specify the criteria of a sql statement. The |
| database adaptor classes contains information on how this Criteria object |
| will be translated to different flavours of sql. |
| </p> |
| |
| <p> |
| Criteria is in effect a map of field names and values that forms the |
| criteria of a query. By default the comparison is equals (=) but you |
| can define any comparison operator (<,>,<=,>=,IN,etc.). |
| </p> |
| |
| <p> |
| Criteria can also be used to do some other sql function like ORDER BY or |
| DISTINCT. If Criteria is too limited for your purposes (which should not |
| happen often) you are still free to use raw sql queries. |
| </p> |
| |
| <p> |
| There is more information on the use of the Criteria class in a seperate <a href ="./criteria-howto.html">Criteria Howto</a>. |
| </p> |
| |
| </section> |
| |
| <section name="ID Broker"> |
| |
| <p> |
| One of the cool features of Peers is the ID Broker. ID Broker is used to |
| automatically create unique primary keys for tables. The ID Broker has |
| support for auto-increment fields like in MySQL; sequence generators like |
| in Oracle or if neither is supported it creates id's from a database |
| table called id_table. |
| </p> |
| |
| <p> |
| You use the MapBuilder to specify which of the techniques you want to use |
| with your system. By default the id_table is used, because it is the |
| only common denominator. |
| </p> |
| |
| <p> |
| For example if you wish to set up the MapBuilder to take advantage of an auto-incrementing Primary Key, you can change the entry in your MapBuilder class from; |
| </p> |
| |
| <source> |
| tMap.setPrimaryKeyMethod(TableMap.IDBROKERTABLE); |
| |
| to |
| |
| tMap.setPrimaryKeyMethod(TableMap.AUTOINCREMENT); |
| </source> |
| |
| <p> |
| The ID Broker is used in the underlying Peer code. After you have set it |
| up you need not worry about it anymore. |
| </p> |
| |
| |
| </section> |
| |
| <section name="Typical Peer Usage"> |
| <p> |
| All the examples on this section will be based on the following schema: |
| </p> |
| |
| <source> |
| create table category ( |
| category_id int not null, |
| name varchar (100) not null, |
| primary key (category_id)); |
| |
| create table item ( |
| item_id int not null, |
| name varchar (100) not null, |
| price int not null, |
| category_id int, |
| primary key (item_id), |
| foreign key (category_id) references category (category_id) |
| on delete set null |
| on update cascade); |
| </source> |
| |
| </section> |
| |
| <section name="Schema Definition"> |
| <p> |
| Peer classes are typically generated by invoking the init task in |
| the ant build file (build.xml) provided with an application generated by |
| the TDK. This file is located in the WEB-INF/build directory of a generated |
| application. |
| </p> |
| |
| <p> |
| Peer class source code is generated based on the project database |
| schema. The definition of this schema is in XML, and the associated DTD |
| is included with the TDK. In the TDK, the project schema is located in |
| the WEB-INF/conf directory of the project in a file called |
| <i>project</i>-schema.xml. |
| </p> |
| |
| <p> |
| For the schema described above, the XML would look something like |
| </p> |
| |
| <source><![CDATA[ |
| <app-data> |
| <database> |
| <table name="category" idMethod="autoincrement"> |
| <column name="category_id" required="true" autoIncrement="true" |
| primaryKey="true" type="INTEGER"/> |
| <column name="name" size="100" type="VARCHAR"/> |
| </table> |
| <table name="item" idMethod="autoincrement"> |
| <column name="item_id" required="true" autoIncrement="true" |
| primaryKey="true" type="INTEGER"/> |
| <column name="name" size="100" type="VARCHAR"/> |
| <column name="price" required="true" type="INTEGER"/> |
| <column name="category_id" required="true" type="INTEGER"/> |
| <foreign-key foreignTable="category"> |
| <reference local="category_id" foreign="category_id"/> |
| </foreign-key> |
| </table> |
| <app-data> |
| <database> |
| ]]> |
| </source> |
| |
| <p> |
| Note that the XML will vary according to the target database - for |
| example, the autoincrement value may be used with mysql, and causes |
| generation of peer source which keeps keys up to date in the associated |
| java objects during inserts, etc. For more information, refer to the |
| DTD which is shipped with Turbine and the TDK. |
| </p> |
| |
| </section> |
| |
| <section name="Selects"> |
| <p> |
| Probably the most common use of Peers is to select data from a database. |
| If we want to extract all the Categories from the database we can use the |
| following code. Because we want all the objects we don't need to add |
| anything special to the Criteria. |
| </p> |
| |
| <source> |
| Criteria crit = new Criteria(); |
| Vector v = CategoryPeer.doSelect (crit); |
| </source> |
| |
| <p> |
| If you want to select all the items of a certain Category you need to add |
| it to the Criteria object. For example we need all the items from the |
| Category with id 2. |
| </p> |
| |
| <source> |
| Criteria crit = new Criteria(); |
| crit.add (ItemPeer.CATEGORY_ID,2); |
| Vector v = ItemPeer.doSelect (crit); |
| </source> |
| |
| <p> |
| After you obtained the Vector of Data Objects you can add it to the |
| Context if you're using WebMacro or Velocity. This will allow you to use |
| #foreach in the template to display all the selected items. |
| </p> |
| |
| </section> |
| |
| <section name="Inserts"> |
| |
| <p> |
| To do an insert we need to add all the fields to the criteria objects. |
| Note that the id field is not added to the Criteria. It is taken care of |
| by the ID BROKER. The object that is returned by doInsert is the id of |
| the newly added row. |
| </p> |
| |
| <source> |
| Criteria crit = new Criteria(); |
| crit.add (CategoryPeer.NAME,"New Category"); |
| Object o = CategoryPeer.doInsert (crit); |
| </source> |
| |
| <p> |
| We can also use the overloaded method to add a new Data Object. Note |
| that once again we don't set the id - the ID BROKER does this for us. |
| </p> |
| |
| <source> |
| Item itm = new Item(); |
| itm.setName ("New Item"); |
| itm.setPrice (100); |
| itm.setCategoryId (1); |
| Object o = ItemPeer.doInsert (itm); |
| </source> |
| |
| <p> |
| I saved the best for last. Say that you want to create a new database |
| entry from an html form. You can use the ParameterParser object (see |
| the getParameters method of RunData in the api docs) to automatically |
| fill in your fields. All that you need to do is to give your html form |
| fields the same name as the corresponding property in the generated Data |
| Object. For example a database field ITEM_ID will cause the generated |
| Data Object to have getItemId and setItemId methods. Here the property |
| name is ItemId, so that should be the form field name. Then you can |
| have code like this: |
| </p> |
| |
| <source> |
| Item itm = new Item() |
| data.getParameters().setProperties(itm); |
| Object o = ItemPeer.doInsert (itm); |
| </source> |
| |
| <p> |
| Updates works pretty much the same as inserts. You just need to call |
| the doUpdate method from your Peer class. Just keep in mind that you |
| must add an id column if you wish to do updates. |
| </p> |
| </section> |
| |
| <section name="Deletes"> |
| <p> |
| Deletes work much in the same way as a select. If you, for example, |
| want to delete the item with id = 3 then you simply add it to the |
| Criteria and call doDelete. |
| </p> |
| |
| <source> |
| Criteria crit = new Criteria(); |
| crit.add (ItemPeer.ITEM_ID,3); |
| ItemPeer.doDelete (crit); |
| </source> |
| </section> |
| |
| <section name="Advanced Peer Techniques"> |
| |
| <p> |
| In this section I'm going to try and explain a bit more about using |
| Peers than just run of the mill selects, inserts and updates. |
| However, this is by no means the be-all and end-all of Peer usage. |
| It is just some ideas that I have found to work well. |
| </p> |
| |
| <p> |
| <strong>Note:</strong> The <a href="../classhierarchy.html">Class |
| Hierarchy</a> document provides more information regarding mapping a |
| hierarchy in the OM/Peer system. |
| </p> |
| |
| </section> |
| |
| <section name="Subclassing"> |
| <p> |
| Usually when we begin to add extra code for Peer classes we end up with |
| the problem of where to add the code. If we add it to the generated |
| BaseItemPeer and BaseCategoryPeer classes we will lose everything if |
| (and trust me this does happen) we need to regenerate these classes. |
| </p> |
| |
| <p> |
| The solution is to add the extensions to the subclasses ItemPeer and |
| CategoryPeer. We can regenerate the Peer classes at any time and still |
| maintain any changes we add. Regenerating the Peers from the XML schema |
| only affects the Base classes. The other classes are left untouched. |
| </p> |
| |
| </section> |
| |
| <section name="Useful Methods"> |
| <p> |
| I found that it saves a bit of duplicate work if you add some utility |
| methods to your Peer class. The first method is very straightforward. |
| It is used to select all the entries from a table and I usually call it |
| doSelectAll(). |
| </p> |
| |
| <source> |
| public class CategoryPeer extends BaseCategoryPeer |
| { |
| static public Vector doSelectAll() throws Exception |
| { |
| Criteria crit = new Criteria(); |
| return = doSelect(crit); |
| } |
| } |
| </source> |
| |
| <p> |
| The next method is used to select all the objects of a database relation. |
| For example the relation between a category and items. Say for example |
| that you routinely need to select all the items of a given category. We |
| add a doSelectForCategory to the ItemPeer class. |
| </p> |
| <source> |
| public class ItemPeer extends BaseItemPeer |
| { |
| static public Vector doSelectForCategory(int categoryid) throws Exception |
| { |
| Criteria crit = new Criteria(); |
| crit.add (CATEGORY_ID,categoryid); |
| return doSelect (crit); |
| } |
| } |
| </source> |
| </section> |
| |
| <section name="Joins and linking objects"> |
| <p> |
| Sometimes you would like to have relations between tables be available |
| in the Peer objects. We defined a foreign key relationship |
| in the Item table in the XML schema. This means getCategory() and |
| setCategory() methods are generated in the BaseItem class. In a |
| relational database the foreign key allows us to access the Category |
| row that is associated with a specific Item row. In the object model, |
| an Item class represents one row from the Item table. The getCategory() |
| method will return a reference to a Category class that represents |
| the associated row from the Category table. |
| </p> |
| |
| <p> |
| A doSelectJoinCategory() method is generated for the BaseItemPeer |
| class. It creates the join between tables and sets the Category |
| reference in the Item class. It can be used like this: |
| </p> |
| <source> |
| // select all Items with their associated Category |
| Criteria crit = new Criteria(); |
| Vector v = ItemPeer.doSelectJoinCategory(crit); |
| // access the Category associated with the first Item in the vector |
| Item itm = (Item)v.elementAt(0); |
| Category cat = itm.getCategory(); |
| </source> |
| <p> |
| We can also constrain the selected rows just as we would with a normal |
| doSelect() method: |
| </p> |
| <source> |
| // select only Items with a category of 2 |
| Criteria crit = new Criteria(); |
| crit.add(Item.CATEGORY_ID, 2); |
| Vector v = ItemPeer.doSelectJoinCategory(crit); |
| // get the name of category 2 |
| Item itm = (Item)v.elementAt(0); |
| Category cat = itm.getCategory(); |
| String name = cat.getName(); |
| </source> |
| </section> |
| |
| <section name="Examples"> |
| <p> |
| This is an example of a complex SQL query and the code you should use |
| to create it: |
| </p> |
| |
| <source><![CDATA[ |
| select * from abc where (a < 1 and b > 2) or ( a > 5 and b < 3) |
| ]]></source> |
| |
| <source><![CDATA[ |
| Criteria crit = new Criteria(); |
| Criteria.Criterion a1 = crit.getNewCriterion(ABC.A, 1, |
| Criteria.LESS_THAN); |
| Criteria.Criterion b2 = crit.getNewCriterion(ABC.B, 2, |
| Criteria.GREATER_THAN); |
| Criteria.Criterion a5 = crit.getNewCriterion(ABC.A, 5, |
| Criteria.GREATER_THAN); |
| Criteria.Criterion b3 = crit.getNewCriterion(ABC.B, 3, |
| Criteria.LESS_THAN); |
| |
| crit.add( a1.and(b2).or(a5.and(b3)) ); |
| ]]></source> |
| |
| <p> |
| There are a lot more examples of how to use the Criteria class in the <a href ="./criteria-howto.html">Criteria Howto</a>. |
| </p> |
| |
| </section> |
| |
| </body> |
| </document> |