package clear.cdb.extjs.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Is used to automatically generate complete set of Java/Hibernate and Ext JS classes * implementing CRUD data synchronization between a database and Ext JS UI; no manual coding * is required. * * Should be applied only to methods returning collections of entities or collections of DTO types. * <p> <table class="innertable"> <tr> <th>Parameter</th><th>Type</th><th>Required</th><th>Description</th> </tr> <tr> <td><code>query</code></td><td>String</td><td>Required</td><td>A JPQL query describing the result set to be fetched from the database by the <code>fill</code> method. The query can contain parameters of the annotated method prefixed with ":"</td> </tr> <tr> <td><code>transferInfo</code></td><td>@JSTransferInfo</td><td>Optional</td><td>Allows to narrow the base type of the return collection to the DTO class that is dynamically created * from the result set metadata. It is applicable when Java return type of the method is a collection based on * a wildcard. * <br> * <br> * Sub-annotation <code>@JSTransferInfo</code> has the following parameters: * <li>type - String. Fully qualified class name of the dynamic return type;</li> * <li>mappedBy - Class. Entity type that will be globally narrowed down to this dynamic return type</li> * <li>generate - boolean (true). Flags to turn on generation of the dynamic DTO</li> * <br> * <br> * In it's complete form, i.e. <code>transferInfo=@JSTransferInfo(type="foo.dto.BarDTO", mappedBy="foo.entity.BarEntity")</code> * it defines a project-level mapping from entity to a newly created DTO. <br>You should use <code>mappedBy</code> only once for a DTO per project, be that in a <code>@CXJPQLMethod</code>- or * <code>@JSGetMethod</code>-based annotation to avoid conflicting re-mappings. Elsewhere use a shorter form : * <pre> * transferInfo=@JSTransferInfo(type="foo.dto.BarDTO") * </pre> * Dynamic generation of the DTO in the context of code>@JSJPQLMethod</code> is based on the properties of the * result set. * </td> </tr> <tr> <td><code>updateInfo</code></td><td>@JSUpdateInfo</td><td>Optional</td> <td>Causes generation of update/insert/delete/sync methods in the service code. <br> <br>Sub-annotation <code>@JSUpdateInfo</code> has the following parameters: * <li>updateEntity - Class. Entity class to use for update. If a query has more then one entity in it's FROM clause, only one entity can be updated;</li> * <li>keyPropertyNames - String. Optional, comma-separated list of properties used to identify original database records in the WHERE clause of the JPQL queries of the generated update/delete/insert * methods. If omitted will default to the property annotated with <code>@Id</code></li> * <li>updatablePropertyNames - String. Optional, comma-separated list of properties that are allowed to be updated. If omitted defaults to all * properties specified in <code>changedPropertyNames</code> of the <a href="http://help.faratasystems.com/en_US/cleartoolkit/reference/flex/4/clear/data/ChangeObject.html">ChangeObject</a> items sent by Flex * code to <code>sync</code> method of the generated service;</li> * <li>autoSyncEnabled - boolean. Optional. If set to <code>true</code> the generated service code will push incoming changes to all * destinations subscribed to the same result set. Default value is <code>false</code></li> *</td> </tr> </table> * </p> * <p><br> *<br>Parameter <code>updateInfo</code>, when set, triggers code generation of four extra methods with the following signatures:<br> <pre> public List<ChangeObject> [fill_method]_sync(List<ChangeObject>); public List<ChangeObject> [fill_method]_deleteItems(List<ChangeObject>); public List<ChangeObject> [fill_method]_updateItems(List<ChangeObject>); public List<ChangeObject> [fill_method]_insertItems(List<ChangeObject>); </pre>, where [fill_method] stands for the name of the original annotated method of the interface * </p> * <p> * For associated properties, i.e. properties annotated with <a href="http://help.faratasystems.com/en_US/cleartoolkit/reference/java/extjs/com/farata/dto2extjs/annotations/JSOneToMany.html">@JSOneToMany</a> * and <a href="http://help.faratasystems.com/en_US/cleartoolkit/reference/java/extjs/com/farata/dto2extjs/annotations/JSManyToOne.html">@JSManyToOne</a> * generated implementation of the <i>fill_method</i> returns <code>null</code> with one exception in <code>@JSManyToOne</code> case : * <li>a <code>@JSOneToMany</code> annotated property is returned as <code>null</code> always and gets populated * on-demand, when the Ext JS code accesses the property first time; </li> * <li>a <code>@JSManyToOne</code> property contains <code>null</code>, * unless it has been explicitly mentioned in the JPQL query</i>. * </p> * <p> * Example 1: <b>"Read-only" scenario with <code>fill</code> method returning entities</b>. The generated service class will implement the <code>getCompanies</code> returning <code>List<com.farata.test.entity.Company></code>. * Since there is no <code>updateInfo</code> the class will not have methods to sync the database with the changes originated from the client: * <pre> * @JSJPQLMethod( * query="SELECT c FROM Company c WHERE c.countryCode=:countryCode" * ) * List<com.farata.test.entity.Company> getCompanies(String countryCode); * </pre> * </p> * <p> * Example 2: <b>"Read-only" scenario with <code>fill</code> method returning <code>java.util.Map</code></b>. Because the return type contains a wildcard * the generated implementation of the <code>getCompanies</code> will be returning list of <code>java.util.Map</code> objects. * Since there is no <code>updateInfo</code> the class will not contain methods to serve client's request to sync the database with the changes * originated from the client: * <pre> * @JSJPQLMethod( * query="SELECT c FROM Company c" * ) * List<?> getCompanies(); * </pre> * </p> * <p> * Example 3: <b>"Read-write" scenario with <code>fill</code> method returning dynamic DTO</b>. The factual return type is a List of * dynamically generated <code>CompanyAssociateDTO</code> object, its properties are shaped by result set of the query: * <code>id</code>, <code>associateName</code>, <code>companyId</code>. Due to <code>mappedBy</code>, nested references * to <code>CompanyAssociate</code> will be automatically replaced with <code>CompanyAssociateDTO</code>. Since <code>updateInfo</code> is provided, CDB will generate Java methods that enable * Ext JS UI to invoke <code>sync</code> method of the store: * <pre> * @JSJPQLMethod( * query="SELECT a.id, a.associateName, a.company.id as companyId FROM CompanyAssociate a", * transferInfo=@JSTransferInfo(type="com.farata.test.dto.CompanyAssociateDTO", mappedBy=CompanyAssociate.class), * updateInfo=@JSUpdateInfo(updateEntity=CompanyAssociate.class) * ) * List<?> getAssociates(); * </pre> * </p> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface JSJPQLMethod { String query(); JSTransferInfo transferInfo() default @JSTransferInfo(type=""); JSUpdateInfo updateInfo() default @JSUpdateInfo(updateEntity=DEFAULT.class); }