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