001package io.ebean;
002
003import java.io.Serializable;
004
005/**
006 * Defines how a relationship is fetched via either normal SQL join,
007 * a eager secondary query, via lazy loading or via eagerly hitting L2 cache.
008 * <p>
009 * <pre>{@code
010 * // Normal fetch join results in a single SQL query
011 * List<Order> list = DB.find(Order.class).fetch("details").findList();
012 *
013 * }</pre>
014 * <p>
015 * Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL queries
016 * </p>
017 * <p>
018 * <pre>{@code
019 *
020 * // This will use 2 SQL queries to build this object graph
021 * List<Order> list =
022 *     DB.find(Order.class)
023 *         .fetch("details", FetchConfig.ofQuery())
024 *         .findList();
025 *
026 * // query 1) find order
027 * // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
028 *
029 * }</pre>
030 *
031 * @author mario
032 * @author rbygrave
033 */
034public class FetchConfig implements Serializable {
035
036  private static final long serialVersionUID = 1L;
037
038  private static final int JOIN_MODE = 0;
039  private static final int QUERY_MODE = 1;
040  private static final int LAZY_MODE = 2;
041  private static final int CACHE_MODE = 3;
042
043  private int mode;
044  private int batchSize;
045  private int hashCode;
046
047  /**
048   * Deprecated - migrate to one of the static factory methods like {@link FetchConfig#ofQuery()}
049   *
050   * Construct using default JOIN mode.
051   */
052  @Deprecated
053  public FetchConfig() {
054    //this.mode = JOIN_MODE;
055    this.batchSize = 100;
056    this.hashCode = 1000;
057  }
058
059  private FetchConfig(int mode, int batchSize) {
060    this.mode = mode;
061    this.batchSize = batchSize;
062    this.hashCode = mode + 10 * batchSize;
063  }
064
065  /**
066   * Return FetchConfig to eagerly fetch the relationship using L2 cache.
067   * <p>
068   * Any cache misses will be loaded by secondary query to the database.
069   */
070  public static FetchConfig ofCache() {
071    return new FetchConfig(CACHE_MODE, 100);
072  }
073
074  /**
075   * Return FetchConfig to eagerly fetch the relationship using a secondary query.
076   */
077  public static FetchConfig ofQuery() {
078    return new FetchConfig(QUERY_MODE, 100);
079  }
080
081  /**
082   * Return FetchConfig to eagerly fetch the relationship using a secondary with a given batch size.
083   */
084  public static FetchConfig ofQuery(int batchSize) {
085    return new FetchConfig(QUERY_MODE, batchSize);
086  }
087
088  /**
089   * Return FetchConfig to lazily load the relationship.
090   */
091  public static FetchConfig ofLazy() {
092    return new FetchConfig(LAZY_MODE, 10);
093  }
094
095  /**
096   * Return FetchConfig to lazily load the relationship specifying the batch size.
097   */
098  public static FetchConfig ofLazy(int batchSize) {
099    return new FetchConfig(LAZY_MODE, batchSize);
100  }
101
102  /**
103   * Return FetchConfig to fetch the relationship using SQL join.
104   */
105  public static FetchConfig ofDefault() {
106    return new FetchConfig(JOIN_MODE, 100);
107  }
108
109  /**
110   * We want to migrate away from mutating FetchConfig to a fully immutable FetchConfig.
111   */
112  private FetchConfig mutate(int mode, int batchSize) {
113    if (batchSize < 1) {
114      throw new IllegalArgumentException("batch size "+batchSize+" must be > 0");
115    }
116    this.mode = mode;
117    this.batchSize = batchSize;
118    this.hashCode = mode + 10 * batchSize;
119    return this;
120  }
121
122  /**
123   * Deprecated - migrate to FetchConfig.ofLazy().
124   */
125  @Deprecated
126  public FetchConfig lazy() {
127    return mutate(LAZY_MODE, 10);
128  }
129
130  /**
131   * Deprecated - migrate to FetchConfig.ofLazy(batchSize).
132   */
133  @Deprecated
134  public FetchConfig lazy(int batchSize) {
135    return mutate(LAZY_MODE, batchSize);
136  }
137
138  /**
139   * Deprecated - migrate to FetchConfig.ofQuery().
140   *
141   * Eagerly fetch the beans in this path as a separate query (rather than as
142   * part of the main query).
143   * <p>
144   * This will use the default batch size for separate query which is 100.
145   */
146  @Deprecated
147  public FetchConfig query() {
148    return mutate(QUERY_MODE, 100);
149  }
150
151  /**
152   * Deprecated - migrate to FetchConfig.ofQuery(batchSize).
153   *
154   * Eagerly fetch the beans in this path as a separate query (rather than as
155   * part of the main query).
156   * <p>
157   * The queryBatchSize is the number of parent id's that this separate query
158   * will load per batch.
159   * </p>
160   * <p>
161   * This will load all beans on this path eagerly unless a {@link #lazy(int)}
162   * is also used.
163   * </p>
164   *
165   * @param batchSize the batch size used to load beans on this path
166   */
167  @Deprecated
168  public FetchConfig query(int batchSize) {
169    return mutate(QUERY_MODE, batchSize);
170  }
171
172  /**
173   * Deprecated - migrate to FetchConfig.ofQuery(batchSize).
174   *
175   * Eagerly fetch the first batch of beans on this path.
176   * This is similar to {@link #query(int)} but only fetches the first batch.
177   * <p>
178   * If there are more parent beans than the batch size then they will not be
179   * loaded eagerly but instead use lazy loading.
180   * </p>
181   *
182   * @param batchSize the number of parent beans this path is populated for
183   */
184  @Deprecated
185  public FetchConfig queryFirst(int batchSize) {
186    return query(batchSize);
187  }
188
189  /**
190   * Deprecated - migrate to FetchConfig.ofCache().
191   *
192   * Eagerly fetch the beans fetching the beans from the L2 bean cache
193   * and using the DB for beans not in the cache.
194   */
195  @Deprecated
196  public FetchConfig cache() {
197    return mutate(CACHE_MODE, 100);
198  }
199
200  /**
201   * Return the batch size for fetching.
202   */
203  public int getBatchSize() {
204    return batchSize;
205  }
206
207  /**
208   * Return true if the fetch should use the L2 cache.
209   */
210  public boolean isCache() {
211    return mode == CACHE_MODE;
212  }
213
214  /**
215   * Return true if the fetch should be a eager secondary query.
216   */
217  public boolean isQuery() {
218    return mode == QUERY_MODE;
219  }
220
221  /**
222   * Return true if the fetch should be a lazy query.
223   */
224  public boolean isLazy() {
225    return mode == LAZY_MODE;
226  }
227
228  /**
229   * Return true if the fetch should try to use SQL join.
230   */
231  public boolean isJoin() {
232    return mode == JOIN_MODE;
233  }
234
235  @Override
236  public boolean equals(Object o) {
237    if (this == o) return true;
238    if (o == null || getClass() != o.getClass()) return false;
239    return (hashCode == ((FetchConfig) o).hashCode);
240  }
241
242  @Override
243  public int hashCode() {
244    return hashCode;
245  }
246}