Difference between revisions of "Base Entity Classes"
(→Generating Base Entities) |
|||
(8 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | Introduction | + | == Introduction == |
− | To support the object-oriented [[Domain Driven Architecture]], there is a set of Java entity classes in the <tt>org.opentaps | + | To support the object-oriented [[Domain Driven Architecture]], there is a set of Java entity classes in the <tt>org.opentaps.base.entities</tt> package for all the entities defined with the ofbiz entity engine, both from the original ofbiz applications and the opentaps applications. The entity classes contain all the fields of the entity, accessor (get/set) methods for each field, and fromMap and toMap methods to convert the Java class to a Map. The only exception is that all floating-point values are automatically returned as BigDecimal, instead of Double. This is done in the base Entity.java |
− | + | == Generating Base Entities == | |
The entity classes are automatically generated using a freemarker template <tt>hot-deploy/opentaps-common/templates/BaseEntity.ftl</tt>, based on the entitymodel.xml definitions for all the entities, including view-entities and fields defined by the extend-entity tags, and the Java types defined in the fieldtype XML files for the entity engine. To generate base entities, from the opentaps directory, | The entity classes are automatically generated using a freemarker template <tt>hot-deploy/opentaps-common/templates/BaseEntity.ftl</tt>, based on the entitymodel.xml definitions for all the entities, including view-entities and fields defined by the extend-entity tags, and the Java types defined in the fieldtype XML files for the entity engine. To generate base entities, from the opentaps directory, | ||
Line 10: | Line 10: | ||
</pre> | </pre> | ||
− | It will clear out all the files in the base entities package, start opentaps and load the delegator, and then regenerate the Java classes based on the current entity definitions. | + | It will clear out all the files in the base entities package, start opentaps and load the delegator, and then regenerate the Java classes based on the current entity definitions. For entities defined with multiple or composite primary keys, a Key class is also automatically generated. |
− | === Using Base Entities | + | A few of the base entities may be needed by foundation classes such as Infrastructure or the HibernateContainer. In such cases, they need to be built as part of the base/ package rather than entities/. You can tell the base entities generator to put them into a special package by modifying <tt>framework/base/config/pojoentities-containers.xml</tt>: |
+ | <property name="baseEntityOutputPath" value="opentaps/opentaps-common/src/base/org/opentaps/base/entities/"/> | ||
+ | <property name="baseEntities" value="OpentapsConfiguration,OpentapsConfigurationType,SequenceValueItem"/> | ||
+ | |||
+ | These special Java classes will be in <tt>opentaps/opentaps-common/src/base/org/opentaps/base/entities/</tt>. The rest will be put into | ||
+ | <property name="entityOutputPath" value="hot-deploy/opentaps-common/src/entities/org/opentaps/base/entities/"/> | ||
+ | |||
+ | == Using Base Entities == | ||
The base entity Java classes could be used as a replacement for the ofbiz GenericEntity/GenericValue objects. To go from a GenericValue to a Java class, use the Repository.loadFromGeneric methods, such as: | The base entity Java classes could be used as a replacement for the ofbiz GenericEntity/GenericValue objects. To go from a GenericValue to a Java class, use the Repository.loadFromGeneric methods, such as: | ||
Line 44: | Line 51: | ||
'''DO NOT MODIFY THESE BASE ENTITY CLASSES.''' They should be automatically generated every time your data model changes, so all your changes will be overwritten. If you have more complex classes, extend these base entity classes and implement the additional methods there. | '''DO NOT MODIFY THESE BASE ENTITY CLASSES.''' They should be automatically generated every time your data model changes, so all your changes will be overwritten. If you have more complex classes, extend these base entity classes and implement the additional methods there. | ||
− | + | == Localization == | |
Backward-compatible localization is supported with two special get methods for all entities. You can specify the field name and a locale with: | Backward-compatible localization is supported with two special get methods for all entities. You can specify the field name and a locale with: | ||
Line 52: | Line 59: | ||
get(fieldName, resourceName, locale); | get(fieldName, resourceName, locale); | ||
− | + | == Entity Relations == | |
The auto generated based entities also provide you with methods to traverse the entity relationships defined in the entitymodel XML files. You can either use the basic getRelated_ methods, which allow you to specify the class name and the relationship name, such as: | The auto generated based entities also provide you with methods to traverse the entity relationships defined in the entitymodel XML files. You can either use the basic getRelated_ methods, which allow you to specify the class name and the relationship name, such as: | ||
Line 58: | Line 65: | ||
Or, you can use the methods which are also automatically generated from the entity model XML definitions for the relationships. For example, if your entity had a one to one relationship defined as: | Or, you can use the methods which are also automatically generated from the entity model XML definitions for the relationships. For example, if your entity had a one to one relationship defined as: | ||
+ | <relation type="one" fk-name="RTN_ITEM_RTN" rel-entity-name="ReturnHeader"> | ||
Then your auto generated based entity would have a method defined as: | Then your auto generated based entity would have a method defined as: | ||
− | ReturnHeader getReturnHeader() | + | public ReturnHeader getReturnHeader() |
Similarly, if you define a one to many relationship, there will be a method which returns a list rather than an object. For example, | Similarly, if you define a one to many relationship, there will be a method which returns a list rather than an object. For example, | ||
+ | <relation type="many" fk-name="RTN_ITEM_OISGIR" rel-entity-name="OrderItemShipGrpInvRes"> | ||
will cause the following method to be created: | will cause the following method to be created: | ||
+ | public List<? extends OrderItemShipGrpInvRes> getOrderItemShipGrpInvReses() | ||
Note that the method names are automatically pluralized if the relationship is one to many. | Note that the method names are automatically pluralized if the relationship is one to many. | ||
Finally, if your relationship has a title, it will also be added to the method, so: | Finally, if your relationship has a title, it will also be added to the method, so: | ||
+ | <relation type="one" fk-name="ORDER_HDR_OFAC" title="Origin" rel-entity-name="Facility"> | ||
will create a method: | will create a method: | ||
+ | public Facility getOriginFacility() | ||
− | + | == Interacting with the Database == | |
The ofbiz specific implementation of the [[Domain_Driven_Architecture#Repository|Repository]] provides you with the following methods for interacting with the database which abstracts the underlying data access layer: | The ofbiz specific implementation of the [[Domain_Driven_Architecture#Repository|Repository]] provides you with the following methods for interacting with the database which abstracts the underlying data access layer: | ||
Line 80: | Line 92: | ||
* getFirst: gets the first Entity from a list, or null if there is no first value in the list | * getFirst: gets the first Entity from a list, or null if there is no first value in the list | ||
− | === Convenience Methods === | + | These methods are not available through the RepositoryInterfaces. They are made publicly available in the ofbiz Repository classes only for convenience, in case you need to access them in your scripts. However, please remember that in programming as in life, too many "conveniences" eventually lead to problems. Therefore, we recommend that as much as possible, you use these methods in your scripts only for prototyping, and then you put the finished find methods into a Repository method and write unit tests for it. |
+ | |||
+ | For example, in your script you can get the ofbiz Repository implementation, and then use it for a query: | ||
+ | <pre> | ||
+ | Repository repository = new org.opentaps.foundation.repository.ofbiz.Repository(infrastructure, user); | ||
+ | List<Order> orders = repository.findList(Order.class, conditions); | ||
+ | </pre> | ||
+ | |||
+ | But unless this script is just a one-time thing, and not terribly important, you should eventually move the find operation into a Java repository class. To keep your repositories manageable, you can push less used methods into specialized repositories. For example, this method could be part of an OrderViewRepository instead of the main OrderRepository where the more commonly used order repository methods are: | ||
+ | <pre> | ||
+ | public class OrderViewRepositoryInterface { | ||
+ | public List<Order> findOrdersBy__(..); | ||
+ | } | ||
+ | |||
+ | public class OrderViewRepository implements OrderViewRepositoryInterface { | ||
+ | public List<Order> findOrdersBy__(..) { | ||
+ | return repository.findList(Order.class, conditions); | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | You should never use these find* methods directly in services or higher-level business logic then your repositories. | ||
+ | |||
+ | == Convenience Methods == | ||
+ | |||
+ | The <tt>Entity</tt> class defines the following static methods: | ||
+ | |||
+ | * <tt>getDistinctFieldValues</tt>: returns a Set of the distinct values for a field name. | ||
+ | * <tt>getFieldValues</tt>: returns a List of the field values for a field name, in the same order as the given entity list. | ||
+ | * <tt>groupByFieldValues:</tt> returns a Map of key => entity where keys are the distinct field values. | ||
+ | * <tt>sumFieldValues</tt>: returns the sum of a given numeric field. | ||
+ | |||
+ | == Changes in 1.4 preview 3 == | ||
− | + | Note that from opentaps 1.4 preview 3 and later, the java package has been changed from <tt>org.opentaps.domain.base.entities</tt> to <tt>org.opentaps.base.entities</tt>. |
Latest revision as of 23:06, 25 June 2010
Contents
Introduction
To support the object-oriented Domain Driven Architecture, there is a set of Java entity classes in the org.opentaps.base.entities package for all the entities defined with the ofbiz entity engine, both from the original ofbiz applications and the opentaps applications. The entity classes contain all the fields of the entity, accessor (get/set) methods for each field, and fromMap and toMap methods to convert the Java class to a Map. The only exception is that all floating-point values are automatically returned as BigDecimal, instead of Double. This is done in the base Entity.java
Generating Base Entities
The entity classes are automatically generated using a freemarker template hot-deploy/opentaps-common/templates/BaseEntity.ftl, based on the entitymodel.xml definitions for all the entities, including view-entities and fields defined by the extend-entity tags, and the Java types defined in the fieldtype XML files for the entity engine. To generate base entities, from the opentaps directory,
$ ant make-base-entities
It will clear out all the files in the base entities package, start opentaps and load the delegator, and then regenerate the Java classes based on the current entity definitions. For entities defined with multiple or composite primary keys, a Key class is also automatically generated.
A few of the base entities may be needed by foundation classes such as Infrastructure or the HibernateContainer. In such cases, they need to be built as part of the base/ package rather than entities/. You can tell the base entities generator to put them into a special package by modifying framework/base/config/pojoentities-containers.xml:
<property name="baseEntityOutputPath" value="opentaps/opentaps-common/src/base/org/opentaps/base/entities/"/> <property name="baseEntities" value="OpentapsConfiguration,OpentapsConfigurationType,SequenceValueItem"/>
These special Java classes will be in opentaps/opentaps-common/src/base/org/opentaps/base/entities/. The rest will be put into
<property name="entityOutputPath" value="hot-deploy/opentaps-common/src/entities/org/opentaps/base/entities/"/>
Using Base Entities
The base entity Java classes could be used as a replacement for the ofbiz GenericEntity/GenericValue objects. To go from a GenericValue to a Java class, use the Repository.loadFromGeneric methods, such as:
Repository repository = new Repository(delegator); List enumerationEntities = repository.loadFromGeneric(Enumeration.class, enumerations);
When you are working from a repository, you should use the loadFromGeneric method which also sets the repository for a new object:
GenericValue value = getDelegator().findByPrimaryKey("Invoice", UtilMisc.toMap("invoiceId", invoiceId)); Invoice invoice = (Invoice) this.loadFromGeneric(Invoice.class, value, this);
These methods uses reflection to access the fromMap method of entity classes. They can create a one object from one GenericValue or a List of objects from a List of GenericValues.
To go from a Java class, you can simply use the toMap method to create a GenericValue, such as:
Enumeration enumeration = new Enumeration(); // set its values GenericValue value = new GenericValue(enumeration.toMap());
For convenience, we have also implemented all the get_(String fieldName) and the set (String fieldName, Object value) methods of the ofbiz GenericEntity/GenericValue, so you can use these classes with the "." notation in freemarker pages. For example, for an object of the Invoice class, you can use
Invoice.getInvoiceId()
or
Invoice.invoiceId
as before.
DO NOT MODIFY THESE BASE ENTITY CLASSES. They should be automatically generated every time your data model changes, so all your changes will be overwritten. If you have more complex classes, extend these base entity classes and implement the additional methods there.
Localization
Backward-compatible localization is supported with two special get methods for all entities. You can specify the field name and a locale with:
get(fieldName, locale);
Or, you can specify the UI labels resource, such as FinancialsUiLabels, to use with:
get(fieldName, resourceName, locale);
Entity Relations
The auto generated based entities also provide you with methods to traverse the entity relationships defined in the entitymodel XML files. You can either use the basic getRelated_ methods, which allow you to specify the class name and the relationship name, such as:
getRelated(ReturnItem.class, "ReturnItem");
Or, you can use the methods which are also automatically generated from the entity model XML definitions for the relationships. For example, if your entity had a one to one relationship defined as:
<relation type="one" fk-name="RTN_ITEM_RTN" rel-entity-name="ReturnHeader">
Then your auto generated based entity would have a method defined as:
public ReturnHeader getReturnHeader()
Similarly, if you define a one to many relationship, there will be a method which returns a list rather than an object. For example,
<relation type="many" fk-name="RTN_ITEM_OISGIR" rel-entity-name="OrderItemShipGrpInvRes">
will cause the following method to be created:
public List<? extends OrderItemShipGrpInvRes> getOrderItemShipGrpInvReses()
Note that the method names are automatically pluralized if the relationship is one to many.
Finally, if your relationship has a title, it will also be added to the method, so:
<relation type="one" fk-name="ORDER_HDR_OFAC" title="Origin" rel-entity-name="Facility">
will create a method:
public Facility getOriginFacility()
Interacting with the Database
The ofbiz specific implementation of the Repository provides you with the following methods for interacting with the database which abstracts the underlying data access layer:
- findOne: finds an Entity by its primary key
- findOneNotNull: like findOne but throws EntityNotFoundException instead of returning null
- findList: finds a list of Entities using the arguments. Similar to findByAnd and findByCondition of the delegator.
- getFirst: gets the first Entity from a list, or null if there is no first value in the list
These methods are not available through the RepositoryInterfaces. They are made publicly available in the ofbiz Repository classes only for convenience, in case you need to access them in your scripts. However, please remember that in programming as in life, too many "conveniences" eventually lead to problems. Therefore, we recommend that as much as possible, you use these methods in your scripts only for prototyping, and then you put the finished find methods into a Repository method and write unit tests for it.
For example, in your script you can get the ofbiz Repository implementation, and then use it for a query:
Repository repository = new org.opentaps.foundation.repository.ofbiz.Repository(infrastructure, user); List<Order> orders = repository.findList(Order.class, conditions);
But unless this script is just a one-time thing, and not terribly important, you should eventually move the find operation into a Java repository class. To keep your repositories manageable, you can push less used methods into specialized repositories. For example, this method could be part of an OrderViewRepository instead of the main OrderRepository where the more commonly used order repository methods are:
public class OrderViewRepositoryInterface { public List<Order> findOrdersBy__(..); } public class OrderViewRepository implements OrderViewRepositoryInterface { public List<Order> findOrdersBy__(..) { return repository.findList(Order.class, conditions); } }
You should never use these find* methods directly in services or higher-level business logic then your repositories.
Convenience Methods
The Entity class defines the following static methods:
- getDistinctFieldValues: returns a Set of the distinct values for a field name.
- getFieldValues: returns a List of the field values for a field name, in the same order as the given entity list.
- groupByFieldValues: returns a Map of key => entity where keys are the distinct field values.
- sumFieldValues: returns the sum of a given numeric field.
Changes in 1.4 preview 3
Note that from opentaps 1.4 preview 3 and later, the java package has been changed from org.opentaps.domain.base.entities to org.opentaps.base.entities.