001package com.avaje.ebean.annotation;
002
003import java.lang.annotation.ElementType;
004import java.lang.annotation.Retention;
005import java.lang.annotation.RetentionPolicy;
006import java.lang.annotation.Target;
007
008import com.avaje.ebean.Query;
009
010/**
011 * Assign to a property to be based on a SQL formula.
012 * <p>
013 * This is typically a SQL Literal value, SQL case statement, SQL function or
014 * similar.
015 * </p>
016 * <p>
017 * Any property based on a formula becomes a read only property.
018 * </p>
019 * <p>
020 * You may also put use the Transient annotation with the Formula annotation.
021 * The effect of the Transient annotation in this case is that the formula will
022 * <b>NOT</b> be included in queries by default - you have to explicitly include
023 * it via {@link Query#select(String)} or {@link Query#fetch(String, String, com.avaje.ebean.FetchConfig)}.
024 * You may want to do this if the Formula is relatively expensive and only want
025 * it included in the query when you explicitly state it.
026 * </p>
027 * 
028 * <pre class="code">
029 * // On the Order &quot;master&quot; bean
030 * // ... a formula using the Order details
031 * // ... sum(order_qty*unit_price)
032 * &#064;Transient
033 * &#064;Formula(select = &quot;_b${ta}.total_amount&quot;, join = &quot;join (select order_id, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) as _b${ta} on _b${ta}.order_id = ${ta}.id&quot;)
034 * Double totalAmount;
035 * 
036 * </pre>
037 * <p>
038 * As the totalAmount formula is also Transient it is not included by default in
039 * queries - it needs to be explicitly included.
040 * </p>
041 * 
042 * <pre>{@code
043 *
044 * // find by Id
045 * Order o1 = Ebean.find(Order.class)
046 *     .select("id, totalAmount")
047 *     .setId(1).findUnique();
048 * 
049 * // find list ... using totalAmount in the where clause
050 * List<Order> list = Ebean.find(Order.class)
051 *     .select("id, totalAmount")
052 *     .where()
053 *     .eq("status", Order.Status.NEW)
054 *     .gt("totalAmount", 10)
055 *     .findList();
056 * 
057 * // as a join from customer
058 * List<Customer> l0 = Ebean.find(Customer.class)
059 *     .select("id, name")
060 *     .fetch("orders", "status, totalAmount")
061 *     .where()
062 *     .gt("id", 0)
063 *     .gt("orders.totalAmount", 10)
064 *     .findList();
065 * 
066 * }</pre>
067 */
068@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
069@Retention(RetentionPolicy.RUNTIME)
070public @interface Formula {
071
072  /**
073   * The SQL to be used in the SELECT part of the SQL to populate a property.
074   */
075  String select();
076
077  /**
078   * OPTIONAL - the SQL to be used in the JOIN part of the SQL to support the
079   * formula.
080   * <p>
081   * This is commonly used to join a 'dynamic view' to support aggregation such
082   * as count, sum etc.
083   * </p>
084   * <p>
085   * The join string should start with either "left outer join" or "join".
086   * </p>
087   * 
088   * <p>
089   * You will almost certainly use the "${ta}" as a place holder for the table
090   * alias of the table you are joining back to (the "base table" of the entity
091   * bean).
092   * </p>
093   * <p>
094   * The example below is used to support a total count of topics created by a
095   * user.
096   * </p>
097   * 
098   * <pre>{@code
099   *
100   * join (select user_id, count(*) as topic_count from f_topic group by user_id) as _tc on _tc.user_id = ${ta}.id
101   *
102   * }</pre>
103   */
104  String join() default "";
105
106}