OFBIZ ENTITY ENGINE COOKBOOK ============================ * Keep your entity names less than 25 characters Oracle, for example, does not like tables with names longer than 30 characters, and OFBIZ will add a "_" between words, so it's best to keep them short. * How relationships work They are defined in entitymodel.xml files for an like this: type= defines the type of the relationship: "one" for one-to-one or "many" for one-to-many from this entity fk-name= is a foreign key name. It is usually good practice to set your own foreign key name, although OFBIZ will auto-generate if it's not there. rel-entity-name= is the name of the related entity. title= is used to distinguish different relationships when there are many relationships between the same two entities. defines the fields for the relationship. field-name= is for the field of this entity to use. If the field on the related entity has a different field name, then rel-field-name= defines the field name in the related entity. You can have many fields serve as part of a key-map. When you access a relationship, you would use .getRelated("") or .getRelatedOne("") with the title+entityName as parameter. getRelated returns a List and is appropriate when it is a "many" relation. .getRelatedOne returns a single GenericVaue and is appropriate for "one" relations. * A few things about view-entities View-entities are very powerful and allow you to create join-like queries, even when your database doesn't support joins. The configuration of your database's join syntax is in entityengine.xml under the join-style attribute of When you link two entities together using the tag, remember 1. The order of the entities you name is important. 2. The default is an inner join (values must be present on both sides.) To do an outer join, use rel-optional="true" If several entities have the same field name, such as statusId, the one from the first entity will be used and the rest tossed out. So if you need the field from one, put its before the others. Alternatively, use tag to give the same field from different entities names, such as: Another alternative is to use inside an as follows: This top-down approach is more compact, which comes in handy for large tables. Alternatively, you can specify one or more inside an . This top-down approach is more compact than specifying the alias for every field in a big table. If you need to do a query like this SELECT count(visitId) FROM ... GROUP BY trackingCodeId WHERE fromDate > '2005-01-01' include field visitId with function="count", trackingCodeId with group-by="true", and fromDate with group-by="false" Then **VERY IMPORTANT** when you do your query, such as delegator.findByCondition, you must specify the fields to select, and you must not specify the field fromDate, or you will get an error. This is why these view-entities can't be viewed from webtools. For an example, look at the view entities at the bottom of applications/marketing/entitydef/entitymodel.xml and the BSH scripts in applications/marketing/webapp/marketing/WEB-INF/actions/reports. * Do I have to define my view-entities in an entitymodel.xml file? No, you can also define them dynamically. A good example is the findParty method in org.ofbiz.party.party.PartyServices * How to build conditions for expiration dates There is a series of very helpful methods called EntityUtil.getFilterByDateExpr which return an EntityConditionList to filter out search results by date. * How to work with large sets of data If you need to select large sets of data, you should use the EntityListIterator instead of the List. For example, if you did List products = delegator.findAll("Product"); You may get yourself a "java.lang.OutOfMemoryError". This is because findAll, findByAnd, findByCondition will try to retrieve all the records into memory as a List. In that case, re-write your query to do return an EntityListIterator then loop through it. For example, this query can be re-written as: productsELI = delegator.findListIteratorByCondition("Product", new EntityExpr("productId", EntityOperator.NOT_EQUAL, null), UtilMisc.toList("productId"), null); Note that the only method for finding an EntityListIterator is a by condition, so you would have to re-write your conditions as EntityExpr (in this case, a dummy one which just says that productId is not null, which should apply to all Product entities, since productId is a not-null primary key field) or EntityConditionList. This method also asks you to fill in the fields to select (in this case just productId) and order by (which I didn't specify with the last null.) You can pass a null EntityCondition to grab all records. However, this does not work across all databases! Beware the use of avanced functionality such as the EntityListIterator with maxdb and other odd databases. * How to use an EntityListIterator When iterating through the EntityListIterator, this is the preferred form: while ((nextProduct = productsELI.next()) != null) { .... // operations on nextProduct } Using the .hasNext() method on EntityListIterator is considered wasteful. When you are done, don't forget to close it: productsELI.close(); * How to do a select distinct The only way we know of to do it with the entity engine is to find a list iterator and use EntityFindOptions to specify it, like this: listIt = delegator.findListIteratorByCondition(entityName, findConditions, null, // havingEntityConditions fieldsToSelectList, fieldsToOrderByList, // This is the key part. The first true here is for "specifyTypeAndConcur" // the second true is for a distinct select. Apparently this is the only way the entity engine can do a distinct query new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true)); In minilang, curiously, it is much easier: