MentaBean

Rev

Rev 203 | Rev 205 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
149 erico 1
package org.mentabean.jdbc;
2
 
151 erico 3
import java.sql.PreparedStatement;
149 erico 4
import java.sql.ResultSet;
151 erico 5
import java.sql.SQLException;
6
import java.util.ArrayList;
152 erico 7
import java.util.HashMap;
149 erico 8
import java.util.Iterator;
151 erico 9
import java.util.List;
152 erico 10
import java.util.Map;
149 erico 11
 
12
import org.mentabean.BeanConfig;
13
import org.mentabean.BeanException;
14
import org.mentabean.DBField;
15
import org.mentabean.sql.Condition;
151 erico 16
import org.mentabean.sql.HasParams;
152 erico 17
import org.mentabean.sql.Sentence;
182 erico 18
import org.mentabean.sql.param.DefaultParamHandler;
151 erico 19
import org.mentabean.sql.param.Param;
182 erico 20
import org.mentabean.sql.param.ParamHandler;
152 erico 21
import org.mentabean.sql.param.ParamValue;
149 erico 22
import org.mentabean.util.PropertiesProxy;
151 erico 23
import org.mentabean.util.SQLUtils;
149 erico 24
 
25
/**
26
 * Fluent QueryBuilder useful to create SQL queries
27
 *
28
 * @author margel
29
 * @author erico
30
 *
31
 */
32
public class QueryBuilder {
33
 
204 erico 34
        private static final String REGEX = "(AND|OR|\\([\\s]*?\\)|WHERE|HAVING)[\\s]*?$";
163 erico 35
        private StringBuilder sb = new StringBuilder();
149 erico 36
        private final AnsiSQLBeanSession session;
151 erico 37
        private List<Object> paramValues = new ArrayList<Object>();
182 erico 38
        private List<Alias<?>> selectAliases;
39
        private List<Alias<?>> createdAliases = new ArrayList<Alias<?>>();
152 erico 40
        private Map<String, Sentence> sentences = new HashMap<String, Sentence>();
153 erico 41
        private Alias aliasFrom;
163 erico 42
        private int parenthesis = 0;
151 erico 43
        private boolean clauseIf;
182 erico 44
        private ParamHandler paramHandler;
149 erico 45
 
46
        protected QueryBuilder(final AnsiSQLBeanSession session) {
47
                this.session = session;
182 erico 48
                this.paramHandler = new DefaultParamHandler();
149 erico 49
        }
50
 
182 erico 51
        protected QueryBuilder(final AnsiSQLBeanSession session, ParamHandler paramHandler) {
52
                this.session = session;
53
                this.paramHandler = paramHandler;
54
        }
55
 
56
        public List<Alias<?>> getCreatedAliases() {
57
                return createdAliases;
58
        }
59
 
150 erico 60
        /**
161 erico 61
         * Builds an initial <i>SELECT</i> statement with given aliases
62
         * @param as - Alias(es) with properties that will be retrieved
63
         * @return A new <code>Select</code> object
150 erico 64
         */
149 erico 65
        public Select select(Alias<?>... as){
66
 
182 erico 67
                return new Select(null, as);
149 erico 68
        }
152 erico 69
 
182 erico 70
        public Select selectDistinct(Alias<?>... as){
71
 
72
                return new Select("DISTINCT", as);
73
        }
74
 
161 erico 75
        /**
168 erico 76
         * Builds an initial <i>SELECT alias FROM alias</i> statement.<br>
77
         * Same as <code>select(alias).from(alias)</code>
78
         * @param as - Alias with properties that will be retrieved
79
         * @return A new <code>From</code> object
80
         * @see #select(Alias...)
81
         * @see Select#from(Alias)
82
         */
83
        public From selectFrom(Alias<?> as){
84
 
169 erico 85
                return select(as).from(as);
168 erico 86
        }
87
 
182 erico 88
        public From selectDistinctFrom(Alias<?> as){
89
 
90
                return new Select("DISTINCT", as).from(as);
91
        }
92
 
168 erico 93
        /**
161 erico 94
         * Builds an initial <i>SELECT</i> statement with given sentences
95
         * @param sentences - Sentence(s) to insert in <i>SELECT</i> clause
96
         * @return A new <code>Select</code> object
97
         */
152 erico 98
        public Select select(Sentence... sentences){
99
 
182 erico 100
                return new Select(null, sentences);
152 erico 101
        }
182 erico 102
 
103
        public Select selectDistinct(Sentence... sentences){
104
 
105
                return new Select("DISTINCT", sentences);
106
        }
151 erico 107
 
150 erico 108
        /**
109
         * Creates a new QueryBuilder with the same session. It's useful to build sub-queries from the main query
110
         * @return The new QueryBuilder instance
111
         */
151 erico 112
        public QueryBuilder subQuery() {
182 erico 113
                QueryBuilder qb = new QueryBuilder(session);
114
                //qb.selectAliases = selectAliases;
115
                qb.createdAliases = createdAliases;
116
                return qb;
149 erico 117
        }
118
 
119
        /**
150 erico 120
         * Creates an alias to be used in this QueryBuilder
149 erico 121
         *
122
         * @param clazz - Bean class that will be mapped to a BeanConfig
123
         * @param alias - String indicating the alia's name
124
         * @return - The alias object
125
         */
169 erico 126
        public <T> Alias<T> aliasTo(Class<? extends T> clazz, String alias){
149 erico 127
 
128
                return new Alias<T>(clazz, alias);
129
        }
151 erico 130
 
149 erico 131
        /**
150 erico 132
         * Creates an alias to be used in this QueryBuilder. The alia's name is the simple name of the bean class
149 erico 133
         *
134
         * @param clazz - Bean class that will be mapped to a BeanConfig
135
         * @return - The alias object
136
         */
169 erico 137
        public <T> Alias<T> aliasTo(Class<? extends T> clazz){
151 erico 138
 
149 erico 139
                return new Alias<T>(clazz, clazz.getSimpleName().toLowerCase());
140
        }
151 erico 141
 
142
        private void appendTable(Alias<?> a) {
143
 
144
                sb.append(a.config.getTableName()).append(' ').append(a.aliasStr);
145
        }
146
 
152 erico 147
        private void append(Param param) {
151 erico 148
 
155 erico 149
                if (param != null)
150
                        sb.append(param.paramInQuery());
152 erico 151
                add(param);
149 erico 152
        }
153
 
182 erico 154
        private void add(Object param) {
149 erico 155
 
182 erico 156
                if (param instanceof HasParams) {
157
                        HasParams hasParams = (HasParams) param;
158
                        Param[] params = hasParams.getParams();
159
                        if (params != null) {
160
                                for (Param p : params) {
161
                                        add(p);
162
                                }
151 erico 163
                        }
182 erico 164
                }else {
165
                        Param p = paramHandler.findBetter(this, param);
166
                        if (p != null && p.values() != null && p.values().length > 0) {
167
 
168
                                for (Object value : p.values()) {
169
                                        if (value != null) {
170
                                                paramValues.add(value);
171
                                        }
172
                                }
173
                        }
149 erico 174
                }
175
        }
176
 
163 erico 177
        private void applyRegex() {
204 erico 178
                remove(REGEX);
149 erico 179
        }
180
 
163 erico 181
        private void remove(String regex) {
182
 
183
                sb = new StringBuilder(sb.toString().replaceAll(regex, ""));
151 erico 184
 
163 erico 185
        }
186
 
187
        private void addAnd() {
188
                if (clauseIf) {
189
                        applyRegex();
190
                        sb.append(" AND ");
151 erico 191
                }
149 erico 192
        }
193
 
163 erico 194
        private void addOr() {
195
                if (clauseIf) {
196
                        applyRegex();
197
                        sb.append(" OR ");
198
                }
199
        }
200
 
201
        private void openPar() {
202
                parenthesis++;
203
                sb.append('(');
204
        }
205
 
206
        private void closePar() {
207
                applyRegex();
208
                parenthesis--;
209
                sb.append(')');
210
                applyRegex();
204 erico 211
                clauseIf = true;
163 erico 212
        }
213
 
182 erico 214
        private Alias<?> findAlias() {
215
 
216
                Object[] instances = PropertiesProxy.getBeanInstances();
217
                if (instances == null || instances.length == 0) throw new IllegalStateException("Cannot find proxy instance!");
218
 
219
                Object instance = instances[0];
220
 
221
                //TODO PropertiesProxy.addBeanInstance(instance);
222
 
223
                for (Alias<?> a : createdAliases) {
224
                        if (a.pxy() == instance)
225
                                return a;
226
                }
227
 
228
                throw new IllegalStateException("Cannot find alias for " + instance);
229
        }
230
 
149 erico 231
        public class Alias<T> {
232
 
233
                private BeanConfig config;
234
                private String aliasStr;
235
                private T pxy;
236
                private String[] returns;
237
                private String[] returnMinus;
155 erico 238
                private Map<Key, Alias> joined = new HashMap<Key, Alias>();
149 erico 239
 
169 erico 240
                private Alias(Class<? extends T> clazz, String aliasStr) {
149 erico 241
 
242
                        this.aliasStr = aliasStr;
243
                        this.config = session.getConfigFor(clazz);
151 erico 244
 
149 erico 245
                        if (this.config == null)
246
                                throw new BeanException("BeanConfig not found for "+clazz);
182 erico 247
 
248
                        this.pxy = (T) PropertiesProxy.create(config.getBeanClass());
249
 
250
                        createdAliases.add(this);
149 erico 251
                }
252
 
155 erico 253
                /**
254
                 * Defines the properties that will return. In other words, <b>only</b> these properties will be populated
255
                 * @param returns
256
                 */
149 erico 257
                public void setReturns(Object... returns){
151 erico 258
 
149 erico 259
                        this.returns = AnsiSQLBeanSession.getProperties(returns);
260
                }
151 erico 261
 
155 erico 262
                /**
263
                 * Defines the properties that will <b>NOT</b> return. In other words, these properties will be not populated
264
                 * @param returns
265
                 */
149 erico 266
                public void setReturnMinus(Object... returns){
151 erico 267
 
149 erico 268
                        this.returnMinus = AnsiSQLBeanSession.getProperties(returns);
269
                }
270
 
150 erico 271
                /**
182 erico 272
                 * Returns the proxy for bean class
161 erico 273
                 * @return The proxy
150 erico 274
                 */
149 erico 275
                public T pxy(){
276
 
277
                        return pxy;
278
                }
279
 
150 erico 280
                /**
281
                 * Convert the given property to database column
282
                 * @param property - The bean property (can be through proxy)
283
                 * @return The database column name
284
                 */
149 erico 285
                public String toColumn(Object property){
286
 
287
                        return session.propertyToColumn(config.getBeanClass(), property, aliasStr);
288
                }
289
 
150 erico 290
                /**
291
                 * Populates the bean according to ResultSet
292
                 * @param rs
293
                 * @param bean
294
                 */
149 erico 295
                public void populateBean(ResultSet rs, T bean){
296
 
150 erico 297
                        session.populateBeanImpl(rs, bean, aliasStr, returns, returnMinus, false);
149 erico 298
                }
153 erico 299
 
300
                public void populateAll(ResultSet rs, T bean) {
154 erico 301
 
155 erico 302
                        populateBean(rs, bean);
303
 
304
                        for (Map.Entry<Key, Alias> m : joined.entrySet()) {
305
 
306
                                //only if alias is in SELECT clause
182 erico 307
                                if (selectAliases.contains(m.getValue())) {
155 erico 308
 
169 erico 309
                                        Object value = session.getPropertyBean(bean, m.getKey().property, m.getKey().forceInstance);
155 erico 310
 
311
                                        if (value != null) {
312
                                                m.getValue().populateAll(rs, value);
153 erico 313
                                        }
314
                                }
155 erico 315
 
153 erico 316
                        }
317
                }
155 erico 318
 
319
                @Override
320
                public String toString() {
321
                        return "Alias "+aliasStr+" of "+config.getBeanClass();
322
                }
323
 
324
                /**
325
                 * Configures a property name to receive data from alias. When <code>forceIntance</code> is <b>true</b>
326
                 * it will force the creation of a new instance for the property, in other words,
327
                 * the value will never be <code>null</code>
328
                 * @param property - Bean property to populate
329
                 * @param forceInstance
330
                 * @param alias - Alias
331
                 */
332
                private void put(Object property, boolean forceInstance, Alias<?> alias) {
333
                        joined.put(new Key().property(property).forceInstance(forceInstance), alias);
334
                }
335
 
336
                private class Key {
337
 
338
                        private String property;
339
                        private boolean forceInstance;
340
 
341
                        public Key property(Object property) {
342
                                this.property = AnsiSQLBeanSession.getProperties(new Object[] {property})[0];
182 erico 343
                                //this.property = PropertiesProxy.getPropertyName();
155 erico 344
                                return this;
345
                        }
346
 
347
                        public Key forceInstance(boolean force) {
348
                                this.forceInstance = force;
349
                                return this;
350
                        }
351
                }
151 erico 352
 
149 erico 353
        }
354
 
355
        public class Select implements Appendable<Select> {
356
 
182 erico 357
                private boolean init = false;
358
 
359
                private Select(String str) {
360
                        sb.append("SELECT "+(str == null || str.isEmpty() ? "" : str+" "));
361
                        selectAliases = new ArrayList<Alias<?>>();
362
                }
363
 
364
                private Select(String str, Alias<?>... as) {
365
                        this(str);
152 erico 366
                        this.add(as);
367
                }
368
 
182 erico 369
                private Select(String str, Sentence... sentences) {
370
                        this(str);
152 erico 371
                        this.add(sentences);
372
                }
373
 
155 erico 374
                /**
375
                 * Add the sentences in <code>SELECT</code> clause
376
                 * @param sentences
377
                 * @return this
378
                 */
152 erico 379
                public Select add(Sentence... sentences) {
380
 
381
                        for (Sentence s : sentences) {
382
 
154 erico 383
                                if (s.getName() == null || s.getName().isEmpty())
384
                                        throw new BeanException("The sentence ("+s.build()+") in SELECT clause must have a name");
385
 
182 erico 386
                                if(init)
152 erico 387
                                        sb.append(",");
388
 
389
                                sb.append(s.build()).append(' ').append(s.getName());
390
                                QueryBuilder.this.sentences.put(s.getName(), s);
391
                                QueryBuilder.this.add(s);
182 erico 392
                                init = true;
152 erico 393
                        }
394
 
395
                        return this;
396
                }
397
 
155 erico 398
                /**
399
                 * Add the alias columns in <code>SELECT</code> clause
400
                 * @param as
401
                 * @return this
402
                 */
152 erico 403
                public Select add(Alias<?>... as) {
149 erico 404
 
405
                        for (Alias<?> alias: as) {
406
 
182 erico 407
                                if(init)
149 erico 408
                                        sb.append(",");
409
 
182 erico 410
                                selectAliases.add(alias);
152 erico 411
                                sb.append(session.buildSelectImpl(alias.config.getBeanClass(), alias.aliasStr, alias.returns, alias.returnMinus, false, true));
182 erico 412
                                init = true;
149 erico 413
                        }
152 erico 414
 
415
                        return this;
149 erico 416
                }
151 erico 417
 
155 erico 418
                /**
419
                 * Creates the <b>FROM</b> keyword for given alias appending the table name in SQL query.
420
                 * @param alias
161 erico 421
                 * @return A new <code>From</code> object
155 erico 422
                 */
149 erico 423
                public From from(Alias<?> alias){
151 erico 424
 
149 erico 425
                        return new From(alias);
426
                }
151 erico 427
 
149 erico 428
                @Override
152 erico 429
                public Select append(Param p) {
151 erico 430
 
152 erico 431
                        QueryBuilder.this.append(p);
149 erico 432
                        return this;
433
                }
434
        }
151 erico 435
 
152 erico 436
        public class From extends Query implements CanOrder, CanLimit, CanGroupBy, Appendable<From> {
151 erico 437
 
149 erico 438
                private From(Alias<?> alias) {
154 erico 439
 
440
                        aliasFrom = alias;
149 erico 441
                        sb.append(" FROM ").append(alias.config.getTableName()).append(" ").append(alias.aliasStr);
442
                }
151 erico 443
 
149 erico 444
                private From() {}
445
 
155 erico 446
                /**
447
                 * Creates a <b>LEFT JOIN</b> sentence using the given alias
448
                 * <br><br>
449
                 * <b>E.g.:</b>
450
                 * <ul>
451
                 * <code>
452
                 *      builder.select(...)<br><ul>
453
                 *              .from(a)<br>
454
                 *              .leftJoin(b)<br>
455
                 *              ...</ul>
456
                 * </code>
457
                 * </ul>
458
                 *
459
                 * @param a - The alias to join
460
                 * @return a new {@link On} object
461
                 */
149 erico 462
                public On leftJoin(Alias<?> a){
154 erico 463
                        return join(a, "LEFT JOIN");
149 erico 464
                }
151 erico 465
 
155 erico 466
                /**
467
                 * Creates a <b>RIGHT JOIN</b> sentence using the given alias
468
                 * <br><br>
469
                 * <b>E.g.:</b>
470
                 * <ul>
471
                 * <code>
472
                 *      builder.select(...)<br><ul>
473
                 *              .from(a)<br>
474
                 *              .rightJoin(b)<br>
475
                 *              ...</ul>
476
                 * </code>
477
                 * </ul>
478
                 *
479
                 * @param a - The alias to join
480
                 * @return a new {@link On} object
481
                 */
149 erico 482
                public On rightJoin(Alias<?> a){
154 erico 483
                        return join(a, "RIGHT JOIN");
149 erico 484
                }
151 erico 485
 
155 erico 486
                /**
487
                 * Creates a <b>JOIN</b> sentence using the given alias
488
                 * <br><br>
489
                 * <b>E.g.:</b>
490
                 * <ul>
491
                 * <code>
492
                 *      builder.select(...)<br><ul>
493
                 *              .from(a)<br>
494
                 *              .join(b)<br>
495
                 *              ...</ul>
496
                 * </code>
497
                 * </ul>
498
                 *
499
                 * @param a - The alias to join
500
                 * @return a new {@link On} object
501
                 */
149 erico 502
                public On join(Alias<?> a){
154 erico 503
                        return join(a, "JOIN");
504
                }
505
 
157 erico 506
                /**
507
                 * Creates a join using the given join type
508
                 * @param a - Alias to join
509
                 * @param joinType - The join type (E.g: <code>"CROSS JOIN"</code> or <code>"LEFT OUTER JOIN"</code>)
510
                 * @return a new {@link On} object
511
                 */
512
                public On join(Alias<?> a, String joinType) {
154 erico 513
                        sb.append(" "+joinType+" ");
149 erico 514
                        appendTable(a);
154 erico 515
                        return new On(a, true);                
149 erico 516
                }
517
 
518
                public Where where() {
519
                        return new Where(true);
520
                }
521
 
522
                @Override
151 erico 523
                public Order orderBy() {
524
 
525
                        return new Order();
149 erico 526
                }
527
 
528
                @Override
182 erico 529
                public Limit limit(Object lim) {
149 erico 530
 
151 erico 531
                        return new Limit(lim);
149 erico 532
                }
533
 
534
                @Override
152 erico 535
                public From append(Param p) {
151 erico 536
 
152 erico 537
                        QueryBuilder.this.append(p);
149 erico 538
                        return this;
539
                }
152 erico 540
 
541
                @Override
542
                public GroupBy groupBy(Alias<?>... aliases) {
543
                        return new GroupBy(aliases);
544
                }
545
 
546
                @Override
159 erico 547
                public GroupBy groupByProp(Alias<?> alias, Object... properties) {
152 erico 548
                        return new GroupBy(alias, properties);
549
                }
550
 
551
                @Override
552
                public GroupBy groupBy() {
553
                        return new GroupBy();
554
                }
181 erico 555
 
556
                @Override
557
                public GroupBy groupBy(Param... p) {
558
                        return new GroupBy(p);
559
                }
149 erico 560
        }
151 erico 561
 
155 erico 562
        /**
563
         * Class representing a 'pos-join' operation
564
         * @author erico
565
         *
566
         */
149 erico 567
        public class On {
151 erico 568
 
153 erico 569
                private Alias<?> aliasPK;
151 erico 570
 
153 erico 571
                private On(Alias<?> aliasPK, boolean init) {
151 erico 572
 
149 erico 573
                        sb.append(init ? " ON " : " AND ");
153 erico 574
                        this.aliasPK = aliasPK;
149 erico 575
                }
151 erico 576
 
149 erico 577
                public OnEquals on(Object property) {
153 erico 578
                        sb.append(aliasPK.toColumn(property));
579
                        return new OnEquals(aliasPK);
149 erico 580
                }
151 erico 581
 
155 erico 582
                public UsingPK pkOf(Alias<?> alias) {
151 erico 583
                        return new UsingPK(alias);
584
                }
585
 
586
        }
587
 
588
        public class UsingPK {
589
 
590
                private Alias<?> aliasPK;
149 erico 591
 
151 erico 592
                private UsingPK(Alias<?> aliasPK) {
593
                        this.aliasPK = aliasPK;
594
                }
595
 
153 erico 596
                public PopulateUsing in(Alias<?> aliasFK) {
151 erico 597
                        buildOn(aliasPK, aliasFK);
153 erico 598
                        return new PopulateUsing(aliasPK, aliasFK);
151 erico 599
                }
600
 
601
                private void buildOn(Alias<?> aPK, Alias<?> aFK){
602
 
603
                        Iterator<DBField> pks = aPK.config.pks();
604
                        DBField df = pks.next();
605
 
606
                        sb.append(aPK.aliasStr).append(".").append(df.getDbName()).append(" = ")
607
                        .append(aFK.aliasStr).append(".").append(df.getDbName());
608
 
609
                        while (pks.hasNext()){
610
 
611
                                df = pks.next();
612
 
613
                                sb.append(" AND ").append(aPK.aliasStr).append(".").append(df.getDbName())
614
                                .append(" = ").append(aFK.aliasStr).append(".").append(df.getDbName());
615
 
616
                        }
617
                }
149 erico 618
        }
153 erico 619
 
620
        public class PopulateUsing extends From {
621
 
622
                private Alias<?> aliasPK, aliasFK;
623
 
624
                public PopulateUsing(Alias<?> aliasPK, Alias<?> aliasFK) {
625
                        this.aliasFK = aliasFK;
626
                        this.aliasPK = aliasPK;
627
                }
628
 
155 erico 629
                /**
630
                 * Defines the property of foreign bean <b>(specified on {@link UsingPK#in(Alias)} method)</b>
631
                 * that will receive the value from alias PK <b>(specified on {@link On#pkOf(Alias)} method)</b> and
632
                 * force the creation of a new instance if bean property is not set.
633
                 * <br><br>
634
                 * <b>E.g.:</b>
635
                 * <ul>
636
                 * <code>
637
                 *      builder.select(user, city)<br><ul>
638
                 *              .from(user)<br>
639
                 *              .join(city)<br>
640
                 *              .pkOf(city).in(user)<br>
641
                 *              <b>.inPropertyForcingInstance(user.pxy().getCity())</b><br>
642
                 *              ...</ul>
643
                 * </code>
644
                 * </ul>
645
                 * @param propertyBean
646
                 * @return this
647
                 * @see Alias#put(Object, boolean, Alias)
648
                 */
649
                public From inPropertyForcingInstance(Object propertyBean) {
650
                        aliasFK.put(propertyBean, true, aliasPK);
651
                        return this;
652
                }
653
 
654
                /**
655
                 * Defines the property of foreign bean <b>(specified on {@link UsingPK#in(Alias)} method)</b>
656
                 * that will receive the value from alias PK <b>(specified on {@link On#pkOf(Alias)} method)</b>
657
                 * <br><br>
658
                 * <b>E.g.:</b>
659
                 * <ul>
660
                 * <code>
661
                 *      builder.select(user, city)<br><ul>
662
                 *              .from(user)<br>
663
                 *              .join(city)<br>
664
                 *              .pkOf(city).in(user)<br>
665
                 *              <b>.inProperty(user.pxy().getCity())</b><br>
666
                 *              ...</ul>
667
                 * </code>
668
                 * </ul>
669
                 * @param propertyBean
670
                 * @return this
671
                 * @see Alias#put(Object, boolean, Alias)
672
                 */
154 erico 673
                public From inProperty(Object propertyBean) {
155 erico 674
                        aliasFK.put(propertyBean, false, aliasPK);
153 erico 675
                        return this;
676
                }
155 erico 677
 
678
                /**
679
                 * Defines the property of primary bean <b>(specified on {@link On#pkOf(Alias)} method)</b>
680
                 * that will receive the value from foreign alias (aliasFK) <b>(specified on {@link UsingPK#in(Alias)} method)</b> and
681
                 * force the creation of a new instance if bean property is not set.
682
                 * <br><br><b>Note: </b>The <b>pkProperty</b> is generally used in 1x1 relationship.
683
                 * <br><br>
684
                 * <b>E.g.:</b>
685
                 * <ul>
686
                 * <code>
687
                 *      builder.select(person, identity)<br><ul>
688
                 *              .from(person)<br>
689
                 *              .join(identity)<br>
690
                 *              .pkOf(person).in(identity)<br>
691
                 *              <b>.pkPropertyForcingInstance(person.pxy().getIdentity())</b><br>
692
                 *              ...</ul>
693
                 * </code>
694
                 * </ul>
695
                 * @param propertyBean
696
                 * @return this
697
                 * @see Alias#put(Object, boolean, Alias)
698
                 */
699
                public From pkPropertyForcingInstance(Object propertyBean) {
700
                        aliasPK.put(propertyBean, true, aliasFK);
701
                        return this;
702
                }
703
 
704
                /**
705
                 * Defines the property of primary bean <b>(specified on {@link On#pkOf(Alias)} method)</b>
706
                 * that will receive the value from foreign alias (aliasFK) <b>(specified on {@link UsingPK#in(Alias)} method)</b>
707
                 * <br><br><b>Note: </b>The <b>pkProperty</b> is generally used in 1x1 relationship.
708
                 * <br><br>
709
                 * <b>E.g.:</b>
710
                 * <ul>
711
                 * <code>
712
                 *      builder.select(person, identity)<br><ul>
713
                 *              .from(person)<br>
714
                 *              .join(identity)<br>
715
                 *              .pkOf(person).in(identity)<br>
716
                 *              <b>.pkProperty(person.pxy().getIdentity())</b><br>
717
                 *              ...</ul>
718
                 * </code>
719
                 * </ul>
720
                 * @param propertyBean
721
                 * @return this
722
                 * @see Alias#put(Object, boolean, Alias)
723
                 */
724
                public From pkProperty(Object propertyBean) {
725
                        aliasPK.put(propertyBean, false, aliasFK);
726
                        return this;
727
                }
153 erico 728
        }
729
 
149 erico 730
        public class OnEquals {
151 erico 731
 
153 erico 732
                private Alias<?> aliasPK;
151 erico 733
 
153 erico 734
                private OnEquals(Alias<?> aliasPK) {
149 erico 735
                        sb.append('=');
153 erico 736
                        this.aliasPK = aliasPK;
149 erico 737
                }
151 erico 738
 
155 erico 739
                /**
182 erico 740
                 * Equals the given property to the property defined in <b>on</b> method
155 erico 741
                 * <br><br>
742
                 * <b>E.g.:</b>
743
                 * <ul>
744
                 * <code>
745
                 *      builder.select(a, b)<br><ul>
746
                 *              .from(a)<br>
747
                 *              .join(b)<br>
748
                 *              .on(b.pxy().getSomething())<br>
182 erico 749
                 *              <b>.eq(a.pxy().getSomething())</b><br>
155 erico 750
                 *              ...</ul>
751
                 * </code>
752
                 * </ul>
753
                 * @param property
161 erico 754
                 * @return A new <code>Equals</code> object
155 erico 755
                 */
182 erico 756
                public Equals eq(Object property) {
153 erico 757
 
182 erico 758
                        Alias<?> alias = findAlias();
759
 
155 erico 760
                        sb.append(alias.toColumn(property));
761
                        return new Equals(this.aliasPK, alias);
149 erico 762
                }
153 erico 763
 
149 erico 764
        }
153 erico 765
 
149 erico 766
        public class Equals extends From {
151 erico 767
 
153 erico 768
                private Alias<?> aliasFK, aliasPK;
151 erico 769
 
153 erico 770
                private Equals(Alias<?> aliasPK, Alias<?> aliasFK) {
771
                        this.aliasPK = aliasPK;
772
                        this.aliasFK = aliasFK;
149 erico 773
                }
153 erico 774
 
155 erico 775
                /**
776
                 * Defines the property of bean <b>specified as alias on {@link OnEquals#eq(Alias, Object)} method</b>
777
                 * that will receive the value from alias <b>specified on {@link From#join(Alias)} method</b> and
778
                 * force the creation of a new instance if bean property is not set.
779
                 * <br><br>
780
                 * <b>E.g.:</b>
781
                 * <ul>
782
                 * <code>
783
                 *      builder.select(user, city)<br><ul>
784
                 *              .from(user)<br>
785
                 *              .join(city)<br>
786
                 *              .on(city.pxy().getId())<br>
787
                 *              .eq(user, user.pxy().getCity().getId())<br>
788
                 *              <b>.inPropertyForcingInstance(user.pxy().getCity())</b><br>
789
                 *              ...</ul>
790
                 * </code>
791
                 * </ul>
792
                 * @param propertyBean
793
                 * @return this
794
                 * @see Alias#put(Object, boolean, Alias)
795
                 */
796
                public Equals eqPropertyForcingInstance(Object propertyBean) {
797
                        aliasFK.put(propertyBean, true, aliasPK);
153 erico 798
                        return this;
799
                }
155 erico 800
 
801
                /**
802
                 * Defines the property of bean <b>specified as alias on {@link OnEquals#eq(Alias, Object)} method</b>
803
                 * that will receive the value from alias <b>specified on {@link From#join(Alias)} method</b>
804
                 * <br><br>
805
                 * <b>E.g.:</b>
806
                 * <ul>
807
                 * <code>
808
                 *      builder.select(user, city)<br><ul>
809
                 *              .from(user)<br>
810
                 *              .join(city)<br>
811
                 *              .on(city.pxy().getId())<br>
812
                 *              .eq(user, user.pxy().getCity().getId())<br>
813
                 *              <b>.inProperty(user.pxy().getCity())</b><br>
814
                 *              ...</ul>
815
                 * </code>
816
                 * </ul>
817
                 * @param propertyBean
818
                 * @return this
819
                 * @see Alias#put(Object, boolean, Alias)
820
                 */
821
                public Equals eqProperty(Object propertyBean) {
822
                        aliasFK.put(propertyBean, false, aliasPK);
823
                        return this;
824
                }
151 erico 825
 
149 erico 826
                public OnEquals and(Object property) {
153 erico 827
                        return new On(aliasPK, false).on(property);
149 erico 828
                }
829
        }
151 erico 830
 
155 erico 831
        public class Where implements Appendable<Where>, HasInitClause<InitClauseWhere> {
151 erico 832
 
149 erico 833
                private Where (boolean init) {
834
                        if (init) {
835
                                sb.append(" WHERE ");
836
                        }
837
                }
155 erico 838
 
839
                @Override
182 erico 840
                public InitClauseWhere clauseIf(boolean clauseIf, Object param) {
155 erico 841
                        QueryBuilder.this.clauseIf = clauseIf;
842
                        return new InitClauseWhere(param);
843
                }
151 erico 844
 
150 erico 845
                @Override
152 erico 846
                public Where append(Param p) {
151 erico 847
 
152 erico 848
                        QueryBuilder.this.append(p);
149 erico 849
                        return this;
850
                }
151 erico 851
 
852
                @Override
182 erico 853
                public InitClauseWhere clause(Object param) {
155 erico 854
                        return clauseIf(true, param);
855
                }
151 erico 856
 
163 erico 857
                /**
858
                 * Insert a left parenthesis '(' in query
859
                 * @return this
860
                 */
861
                public Where openPar() {
862
                        QueryBuilder.this.openPar();
863
                        return this;
864
                }
151 erico 865
 
149 erico 866
        }
151 erico 867
 
150 erico 868
        public class InitClauseWhere extends InitClause implements Appendable<InitClauseWhere>, HasEndClause<EndClauseWhere> {
151 erico 869
 
182 erico 870
                private InitClauseWhere(Object param) {
155 erico 871
 
872
                        super(param);
873
                }
156 erico 874
 
149 erico 875
                @Override
152 erico 876
                public InitClauseWhere append(Param p) {
151 erico 877
 
152 erico 878
                        QueryBuilder.this.append(p);
149 erico 879
                        return this;
880
                }
151 erico 881
 
150 erico 882
                @Override
883
                public EndClauseWhere condition(String condition) {
151 erico 884
 
150 erico 885
                        return new EndClauseWhere(condition);
149 erico 886
                }
151 erico 887
 
150 erico 888
                @Override
889
                public EndClauseWhere condition(Condition condition) {
151 erico 890
 
150 erico 891
                        return new EndClauseWhere(condition);
149 erico 892
                }
151 erico 893
 
149 erico 894
        }
151 erico 895
 
150 erico 896
        public class InitClauseHaving extends InitClause implements Appendable<InitClauseHaving>, HasEndClause<EndClauseHaving> {
155 erico 897
 
182 erico 898
                private InitClauseHaving(Object param) {
155 erico 899
 
900
                        super(param);
901
                }
151 erico 902
 
150 erico 903
                @Override
152 erico 904
                public InitClauseHaving append(Param p) {
151 erico 905
 
152 erico 906
                        QueryBuilder.this.append(p);
150 erico 907
                        return this;
908
                }
151 erico 909
 
150 erico 910
                @Override
911
                public EndClauseHaving condition(String condition) {
151 erico 912
 
150 erico 913
                        return new EndClauseHaving(condition);
914
                }
151 erico 915
 
150 erico 916
                @Override
917
                public EndClauseHaving condition(Condition condition) {
151 erico 918
 
150 erico 919
                        return new EndClauseHaving(condition);
920
                }
151 erico 921
 
150 erico 922
        }
151 erico 923
 
150 erico 924
        public class InitClause {
155 erico 925
 
182 erico 926
                private InitClause (Object param) {
155 erico 927
 
182 erico 928
                        Param p = paramHandler.findBetter(QueryBuilder.this, param);
929
 
151 erico 930
                        if (clauseIf) {
182 erico 931
//                              System.out.println(p.getClass()+" - "+p.paramInQuery());
932
//                              if (p.values() != null)
933
//                              for (Object o : p.values()) {
934
//                                      if (o != null)
935
//                                      System.out.println("type: "+o.getClass());
936
//                              }
937
 
938
                                add(p);
939
                                sb.append(' ').append(p.paramInQuery());
151 erico 940
                        }
155 erico 941
 
150 erico 942
                }
151 erico 943
 
150 erico 944
        }
151 erico 945
 
152 erico 946
        public class EndClauseWhere extends EndClause implements Appendable<EndClauseWhere>, CanGroupBy {
151 erico 947
 
150 erico 948
                private EndClauseWhere(Condition condition) {
151 erico 949
 
950
                        if (clauseIf) {
951
                                add(condition);
952
                                init(condition.build());
953
                        }
150 erico 954
                }
151 erico 955
 
150 erico 956
                private EndClauseWhere(String condition) {
151 erico 957
 
958
                        if (clauseIf) {
959
                                init(condition);
960
                        }
150 erico 961
                }
163 erico 962
 
963
                /**
964
                 * Insert a left parenthesis '(' in query
965
                 * @return this
966
                 */
967
                public EndClauseWhere openPar() {
968
                        QueryBuilder.this.openPar();
969
                        return this;
970
                }
971
 
972
                /**
973
                 * Insert a right parenthesis ')' in query
974
                 * @return this
975
                 */
976
                public EndClauseWhere closePar() {
977
                        QueryBuilder.this.closePar();
978
                        return this;
979
                }
151 erico 980
 
149 erico 981
                public Where and() {
151 erico 982
 
163 erico 983
                        addAnd();
149 erico 984
                        return new Where(false);
985
                }
151 erico 986
 
149 erico 987
                public Where or() {
156 erico 988
 
163 erico 989
                        addOr();
149 erico 990
                        return new Where(false);
991
                }
151 erico 992
 
149 erico 993
                @Override
152 erico 994
                public EndClauseWhere append(Param p) {
151 erico 995
 
152 erico 996
                        QueryBuilder.this.append(p);
150 erico 997
                        return this;
998
                }
151 erico 999
 
152 erico 1000
                @Override
1001
                public GroupBy groupBy(Alias<?>... aliases) {
1002
                        return new GroupBy(aliases);
1003
                }
181 erico 1004
 
1005
                @Override
1006
                public GroupBy groupBy(Param... params) {
1007
                        return new GroupBy(params);
1008
                }
152 erico 1009
 
1010
                @Override
159 erico 1011
                public GroupBy groupByProp(Alias<?> alias, Object... properties) {
152 erico 1012
                        return new GroupBy(alias, properties);
1013
                }
1014
 
1015
                @Override
1016
                public GroupBy groupBy() {
1017
                        return new GroupBy();
1018
                }
1019
 
150 erico 1020
        }
151 erico 1021
 
150 erico 1022
        public class EndClauseHaving extends EndClause implements Appendable<EndClauseHaving> {
151 erico 1023
 
150 erico 1024
                private EndClauseHaving(Condition condition) {
151 erico 1025
 
1026
                        if (clauseIf) {
1027
                                add(condition);
1028
                                init(condition.build());
1029
                        }
150 erico 1030
                }
151 erico 1031
 
150 erico 1032
                private EndClauseHaving(String condition) {
151 erico 1033
 
1034
                        if (clauseIf) {
1035
                                init(condition);
1036
                        }
150 erico 1037
                }
163 erico 1038
 
1039
                /**
1040
                 * Insert a left parenthesis '(' in query
1041
                 * @return this
1042
                 */
1043
                public EndClauseHaving openPar() {
1044
 
1045
                        QueryBuilder.this.openPar();
1046
                        return this;
1047
                }
1048
 
1049
                /**
1050
                 * Insert a right parenthesis ')' in query
1051
                 * @return this
1052
                 */
1053
                public EndClauseHaving closePar() {
1054
 
1055
                        QueryBuilder.this.closePar();
1056
                        return this;
1057
                }
151 erico 1058
 
150 erico 1059
                public Having and() {
155 erico 1060
 
163 erico 1061
                        addAnd();
150 erico 1062
                        return new Having(false);
1063
                }
151 erico 1064
 
150 erico 1065
                public Having or() {
155 erico 1066
 
163 erico 1067
                        addOr();
150 erico 1068
                        return new Having(false);
1069
                }
151 erico 1070
 
150 erico 1071
                @Override
152 erico 1072
                public EndClauseHaving append(Param p) {
151 erico 1073
 
152 erico 1074
                        QueryBuilder.this.append(p);
150 erico 1075
                        return this;
1076
                }
151 erico 1077
 
150 erico 1078
        }
151 erico 1079
 
1080
        public abstract class EndClause extends Query implements CanOrder, CanLimit {
1081
 
150 erico 1082
                protected void init(String condition) {
151 erico 1083
 
150 erico 1084
                        sb.append(' ').append(condition);
1085
                }
151 erico 1086
 
150 erico 1087
                @Override
149 erico 1088
                public Order orderBy() {
151 erico 1089
 
1090
                        return new Order();
149 erico 1091
                }
1092
 
1093
                @Override
182 erico 1094
                public Limit limit(Object lim) {
151 erico 1095
 
1096
                        return new Limit(lim);
149 erico 1097
                }
1098
 
151 erico 1099
        }
149 erico 1100
 
151 erico 1101
        public class Limit extends Query implements CanOrder, Appendable<Limit> {
1102
 
182 erico 1103
                private Limit(Object lim) {
151 erico 1104
 
182 erico 1105
                        Param param = paramHandler.findBetter(QueryBuilder.this, lim);
1106
 
1107
                        if (param != null) {
163 erico 1108
                                applyRegex();
182 erico 1109
                                add(param);
1110
 
1111
                                Object numberObj = paramValues.get(paramValues.size()-1);
1112
                                if (numberObj instanceof Number) {
1113
                                        Number numberLimit = (Number) numberObj;
1114
                                        if (numberLimit.longValue() <= 0) {
1115
                                                paramValues.remove(paramValues.size()-1);
1116
                                                return;
1117
                                        }
1118
                                }
1119
 
1120
                                sb.append(" LIMIT ").append(param.paramInQuery());
151 erico 1121
                        }
149 erico 1122
                }
152 erico 1123
 
149 erico 1124
                @Override
1125
                public Order orderBy() {
151 erico 1126
 
1127
                        return new Order();
149 erico 1128
                }
151 erico 1129
 
1130
                public Offset offset(Integer offset) {
1131
 
149 erico 1132
                        return new Offset(offset);
1133
                }
1134
 
156 erico 1135
                public Offset offset(Param param) {
149 erico 1136
 
156 erico 1137
                        return new Offset(param);
149 erico 1138
                }
1139
 
1140
                @Override
152 erico 1141
                public Limit append(Param p) {
151 erico 1142
 
152 erico 1143
                        QueryBuilder.this.append(p);
149 erico 1144
                        return this;
1145
                }
1146
        }
1147
 
151 erico 1148
        public class Offset extends Query implements Appendable<Offset> {
1149
 
163 erico 1150
                public Offset(Number offset) {
1151
                        this(offset != null && offset.longValue() > 0 ? new ParamValue(offset) : null);
149 erico 1152
                }
1153
 
156 erico 1154
                public Offset(Param param) {
149 erico 1155
 
163 erico 1156
                        if (param != null) {
1157
                                add(param);
1158
                                sb.append(" OFFSET ").append(param.paramInQuery());
1159
                        }
149 erico 1160
                }
151 erico 1161
 
1162
                public Order orderBy() {
1163
 
1164
                        return new Order();
1165
                }
1166
 
149 erico 1167
                @Override
152 erico 1168
                public Offset append(Param p) {
151 erico 1169
 
152 erico 1170
                        QueryBuilder.this.append(p);
149 erico 1171
                        return this;
1172
                }
1173
 
1174
        }
151 erico 1175
 
154 erico 1176
        public class Order implements Appendable<Order> {
151 erico 1177
 
149 erico 1178
                private Order() {
151 erico 1179
 
163 erico 1180
                        applyRegex();
149 erico 1181
                        sb.append(" ORDER BY ");                       
1182
                }
154 erico 1183
 
1184
                public Ordering asc(Param param) {
1185
 
1186
                        return new Ordering().asc(param);
1187
                }
1188
 
1189
                public Ordering desc(Param param) {
1190
 
1191
                        return new Ordering().desc(param);
1192
                }
151 erico 1193
 
154 erico 1194
                public Ordering asc(Alias<?> alias, Object... properties){
1195
 
1196
                        return new Ordering().asc(alias, properties);
1197
                }
1198
 
1199
                public Ordering desc(Alias<?> alias, Object... properties){
1200
 
1201
                        return new Ordering().desc(alias, properties);
1202
                }
1203
 
1204
                @Override
1205
                public Order append(Param p) {
1206
 
1207
                        QueryBuilder.this.append(p);
1208
                        return this;
1209
                }
1210
        }
1211
 
1212
        public class Ordering extends Query implements CanLimit, Appendable<Ordering> {
1213
 
1214
                private boolean alreadyOrder = false;
1215
 
1216
                private void initOrder() {
1217
 
1218
                        if (alreadyOrder) {
1219
                                sb.append(',');
1220
                        }
1221
 
1222
                        alreadyOrder = true;
1223
                }
1224
 
1225
                public Ordering asc(Param param) {
1226
 
1227
                        initOrder();
1228
                        add(param);
1229
                        sb.append(param.paramInQuery()).append(" ASC ");
1230
                        return this;
1231
                }
1232
 
1233
                public Ordering desc(Param param) {
1234
 
1235
                        initOrder();
1236
                        add(param);
1237
                        sb.append(param.paramInQuery()).append(" DESC ");
1238
                        return this;
1239
                }
1240
 
1241
                public Ordering asc(Alias<?> alias, Object... properties){
149 erico 1242
                        iterateOrderBy(" ASC ", alias, properties);
1243
                        return this;
1244
                }
1245
 
154 erico 1246
                public Ordering desc(Alias<?> alias, Object... properties){
149 erico 1247
                        iterateOrderBy(" DESC ", alias, properties);
1248
                        return this;
1249
                }
154 erico 1250
 
149 erico 1251
                @Override
182 erico 1252
                public Limit limit(Object lim) {
151 erico 1253
 
1254
                        return new Limit(lim);
149 erico 1255
                }
1256
 
1257
                private void iterateOrderBy(String orderType, Alias<?> alias, Object[] properties){
151 erico 1258
 
149 erico 1259
                        String[] props = AnsiSQLBeanSession.getProperties(properties);
151 erico 1260
 
154 erico 1261
                        initOrder();
151 erico 1262
 
149 erico 1263
                        for(String prop : props){
151 erico 1264
 
149 erico 1265
                                sb.append(alias.toColumn(prop)).append(orderType).append(",");
1266
                        }
151 erico 1267
 
149 erico 1268
                        sb.setCharAt(sb.length()-1, ' ');
1269
                }
1270
 
1271
                @Override
154 erico 1272
                public Ordering append(Param p) {
152 erico 1273
                        QueryBuilder.this.append(p);
149 erico 1274
                        return this;
1275
                }
1276
        }
151 erico 1277
 
152 erico 1278
        public class GroupBy extends Query implements Appendable<GroupBy>, CanLimit, CanOrder {
151 erico 1279
 
150 erico 1280
                private void init() {
152 erico 1281
 
163 erico 1282
                        applyRegex();
150 erico 1283
                        sb.append(!sb.toString().endsWith(" GROUP BY ") ? " GROUP BY " : ",");
1284
                }
151 erico 1285
 
152 erico 1286
                private GroupBy(Alias<?> alias, Object... properties) {
1287
                        init();
150 erico 1288
                        add(alias, properties);
1289
                }
151 erico 1290
 
152 erico 1291
                private GroupBy(Alias<?>... alias) {
1292
                        init();
150 erico 1293
                        add(alias);
1294
                }
152 erico 1295
 
181 erico 1296
                private GroupBy(Param... params) {
1297
                        init();
1298
                        add(params);
1299
                }
1300
 
152 erico 1301
                private GroupBy() {
1302
                        init();
182 erico 1303
                        add(selectAliases.toArray(new Alias<?>[0]));
152 erico 1304
                }
151 erico 1305
 
150 erico 1306
                public GroupBy add(Alias<?> alias, Object... properties) {
152 erico 1307
 
156 erico 1308
                        if (!sb.toString().endsWith(" GROUP BY ")) {
152 erico 1309
                                sb.append(',');
156 erico 1310
                        }
1311
 
152 erico 1312
                        sb.append(session.buildSelectImpl(alias.config.getBeanClass(), alias.aliasStr,
1313
                                        AnsiSQLBeanSession.getProperties(properties), null, false, false));
156 erico 1314
 
150 erico 1315
                        return this;
1316
                }
151 erico 1317
 
152 erico 1318
                public GroupBy add(Alias<?>... aliases) {
1319
 
1320
                        for (Alias<?> alias : aliases) {
1321
 
1322
                                if (!sb.toString().endsWith(" GROUP BY "))
1323
                                        sb.append(',');
1324
 
1325
                                sb.append(session.buildSelectImpl(alias.config.getBeanClass(), alias.aliasStr,
1326
                                                alias.returns, alias.returnMinus, false, false));
1327
                        }
1328
 
150 erico 1329
                        return this;
1330
                }
181 erico 1331
 
1332
                public GroupBy add(Param... params) {
1333
 
1334
                        for (Param p : params) {
1335
                                if (!sb.toString().endsWith(" GROUP BY "))
1336
                                        sb.append(',');
1337
 
1338
                                append(p);
1339
                        }
1340
 
1341
                        return this;
1342
                }
151 erico 1343
 
150 erico 1344
                public Having having() {
1345
                        return new Having(true);
1346
                }
1347
 
1348
                @Override
152 erico 1349
                public GroupBy append(Param p) {
1350
                        QueryBuilder.this.append(p);
150 erico 1351
                        return this;
1352
                }
152 erico 1353
 
1354
                @Override
1355
                public Order orderBy() {
1356
                        return new Order();
1357
                }
1358
 
1359
                @Override
182 erico 1360
                public Limit limit(Object lim) {
152 erico 1361
                        return new Limit(lim);
1362
                }
1363
 
149 erico 1364
        }
151 erico 1365
 
155 erico 1366
        public class Having extends Query implements Appendable<Having>, HasInitClause<InitClauseHaving>{
151 erico 1367
 
150 erico 1368
                private Having (boolean init) {
1369
                        if (init) {
1370
                                sb.append(" HAVING ");
1371
                        }
1372
                }
151 erico 1373
 
150 erico 1374
                @Override
182 erico 1375
                public InitClauseHaving clauseIf(boolean clauseIf, Object param){
156 erico 1376
 
155 erico 1377
                        QueryBuilder.this.clauseIf = clauseIf;
1378
                        return new InitClauseHaving(param);
1379
                }
1380
 
1381
                @Override
152 erico 1382
                public Having append(Param p) {
1383
                        QueryBuilder.this.append(p);
150 erico 1384
                        return this;
1385
                }
1386
 
1387
                @Override
182 erico 1388
                public InitClauseHaving clause(Object param) {
155 erico 1389
                        return clauseIf(true, param);
1390
                }
151 erico 1391
 
163 erico 1392
                /**
1393
                 * Insert a left parenthesis '(' in query
1394
                 * @return this
1395
                 */
1396
                public Having openPar() {
1397
                        QueryBuilder.this.openPar();
1398
                        return this;
1399
                }
151 erico 1400
 
150 erico 1401
        }
151 erico 1402
 
155 erico 1403
        public interface HasInitClause<T extends InitClause> {
151 erico 1404
 
155 erico 1405
                /**
1406
                 * Insert the param as a clause in query (same of <code>clauseIf(true, param)</code>).
1407
                 * <p>
1408
                 * <b>E.g.:</b>
1409
                 * <ul>
1410
                 * <code>
1411
                 *              ...<br>
1412
                 *              .where()<br>
1413
                 *              .clause(new ParamNative("anything"))<br>
1414
                 *              .condition(...)<br>
1415
                 *              ...<br>
1416
                 *
1417
                 * </code>
1418
                 * </ul>
1419
                 * </p>
1420
                 * @param param
1421
                 */
182 erico 1422
                public T clause(Object param);
155 erico 1423
 
1424
                /**
1425
                 * Insert the param as a clause in query if and only if the flag <b>clauseIf</b> is <b>true</b>
1426
                 * @param clauseIf - Flag indicating if this clause will be inserted in SQL query
1427
                 * @param param
1428
                 * @see #clause(Param)
1429
                 */
182 erico 1430
                public T clauseIf(boolean clauseIf, Object param);
150 erico 1431
 
1432
        }
151 erico 1433
 
150 erico 1434
        public interface HasEndClause<T extends EndClause> {
151 erico 1435
 
150 erico 1436
                public T condition(String condition);
151 erico 1437
 
150 erico 1438
                public T condition(Condition condition);
151 erico 1439
 
150 erico 1440
        }
151 erico 1441
 
149 erico 1442
        public interface CanOrder {
151 erico 1443
 
149 erico 1444
                public Order orderBy();
1445
        }
151 erico 1446
 
149 erico 1447
        public interface CanLimit {
151 erico 1448
 
182 erico 1449
                public Limit limit(Object lim);
149 erico 1450
        }
151 erico 1451
 
150 erico 1452
        public interface CanGroupBy {
151 erico 1453
 
152 erico 1454
                public GroupBy groupBy(Alias<?>... aliases);
181 erico 1455
 
1456
                public GroupBy groupBy(Param... p);
151 erico 1457
 
159 erico 1458
                public GroupBy groupByProp(Alias<?> alias, Object... properties);
152 erico 1459
 
156 erico 1460
                /**
1461
                 * Group by all aliases fields used in <i>SELECT</i> clause
1462
                 * @return this
1463
                 */
152 erico 1464
                public GroupBy groupBy();
1465
 
150 erico 1466
        }
151 erico 1467
 
149 erico 1468
        public interface Appendable<T> {
151 erico 1469
 
156 erico 1470
                /**
1471
                 * Appends the parameter directly in query
1472
                 * @param p - Param
1473
                 * @return this
1474
                 */
152 erico 1475
                public T append(Param p);
149 erico 1476
        }
1477
 
182 erico 1478
        public void finish() {
1479
 
1480
                for (Alias<?> a : selectAliases) {
1481
                        a.joined.clear();
1482
                }
1483
 
1484
                paramValues.clear();
1485
                sb = new StringBuilder();                      
1486
        }
1487
 
155 erico 1488
        /**
1489
         * Represents a query ready to execute
1490
         * @author erico
1491
         *
1492
         */
151 erico 1493
        public class Query {
1494
 
1495
                private Query() {}
1496
 
155 erico 1497
                /**
156 erico 1498
                 * Returns a <code>PreparedStatement</code> setting all given parameters in order.
155 erico 1499
                 *
1500
                 * @param params
161 erico 1501
                 * @return A <code>PreparedStatement</code> using this session connection
155 erico 1502
                 * @see #getSQL()
1503
                 * @see SQLUtils#prepare(java.sql.Connection, String, Object...)
1504
                 */
182 erico 1505
                private PreparedStatement prepare(Object... params) {
151 erico 1506
 
1507
                        try {
1508
 
158 erico 1509
                                PreparedStatement ppst = SQLUtils.prepare(session.getConnection(), getSQL(), params);
1510
 
1511
                                if (AnsiSQLBeanSession.DEBUG_NATIVE) {
1512
                                        System.out.println("CUSTOM QUERY (NATIVE): "+ppst);
1513
                                }
1514
 
1515
                                return ppst;
1516
 
151 erico 1517
                        } catch (SQLException e) {
1518
 
1519
                                throw new BeanException("Error preparing statement", e);
1520
                        }
1521
                }
1522
 
156 erico 1523
                /**
1524
                 * Prepares a statement with paramValues to execute the query manually.
161 erico 1525
                 * @return A <code>PreparedStatement</code> using this session connection
156 erico 1526
                 */
151 erico 1527
                public PreparedStatement prepare() {
1528
 
1529
                        return prepare(paramValues.toArray());
1530
                }
1531
 
156 erico 1532
                /**
1533
                 * Executes the query returning a <code>List</code> of beans declared in <b>FROM</b> clause.
1534
                 *
161 erico 1535
                 * @return A list containing all beans retrieved by <code>ResultSet</code>
156 erico 1536
                 * @see Alias#populateAll(ResultSet, Object)
1537
                 */
153 erico 1538
                public <T> List<T> executeQuery() {
1539
 
1540
                        PreparedStatement ppst = null;
1541
 
1542
                        try {
1543
                                ppst = prepare();
1544
 
1545
                                ResultSet rs = ppst.executeQuery();
1546
 
1547
                                List<T> list = new ArrayList<T>();
1548
                                T bean;
1549
 
1550
                                while (rs.next()) {
154 erico 1551
 
153 erico 1552
                                        bean = (T) aliasFrom.config.getBeanClass().newInstance();
1553
                                        aliasFrom.populateAll(rs, bean);
154 erico 1554
 
1555
                                        for (Sentence s : sentences.values()) {
169 erico 1556
                                                session.injectValue(bean, s.getProperty(),
154 erico 1557
                                                                s.getValue(rs), s.getReturnType().getTypeClass());
1558
                                        }
1559
 
153 erico 1560
                                        list.add(bean);
1561
                                }
1562
 
1563
                                return list;
1564
 
1565
                        } catch (Exception e) {
1566
 
181 erico 1567
                                throw new BeanException("Unable to execute query from QueryBuilder\n"+
1568
                                                e.getMessage(), e);
153 erico 1569
                        }finally {
1570
 
182 erico 1571
                                finish();
1572
 
153 erico 1573
                                SQLUtils.close(ppst);
1574
                        }
1575
                }
1576
 
158 erico 1577
                /**
1578
                 * Executes the query returning a single value according returnType of sentence in query.
161 erico 1579
                 * @return The value returned by query
158 erico 1580
                 */
1581
                public <T> T executeSentence() {
1582
 
1583
                        if (sentences.values().size() != 1)
1584
                                throw new BeanException("This query must have exactly one sentence to execute");
1585
 
1586
                        PreparedStatement ppst = null;
1587
 
1588
                        try {
1589
                                ppst = prepare();
1590
 
1591
                                ResultSet rs = ppst.executeQuery();
1592
 
1593
                                if (rs.next()) {
1594
 
1595
                                        if (!rs.isLast()) {
1596
                                                throw new BeanException("The query returns more than one result");
1597
                                        }
1598
                                        Sentence s = sentences.values().iterator().next();
160 erico 1599
                                        return (T) s.getValue(rs);
158 erico 1600
 
1601
                                }
1602
 
203 erico 1603
                                return null;
158 erico 1604
 
1605
                        } catch (Exception e) {
1606
 
181 erico 1607
                                throw new BeanException("Unable to execute sentence from QueryBuilder\n"+
1608
                                                e.getMessage(), e);
158 erico 1609
                        }finally {
182 erico 1610
 
1611
                                finish();
158 erico 1612
 
1613
                                SQLUtils.close(ppst);
1614
                        }
1615
                }
1616
 
152 erico 1617
                @SuppressWarnings("unchecked")
1618
                public <T> T getValueFromResultSet(ResultSet rs, String name) throws SQLException {
1619
 
1620
                        Sentence s = sentences.get(name);
1621
 
1622
                        if (s == null)
1623
                                throw new BeanException("The sentence name '"+name+"' is not included in query");
1624
 
1625
                        return (T) s.getValue(rs);
1626
                }
1627
 
155 erico 1628
                /**
1629
                 * Returns the SQL generated by QueryBuilder
161 erico 1630
                 * @return The String SQL
155 erico 1631
                 */
151 erico 1632
                public String getSQL() {
1633
 
163 erico 1634
                        if (parenthesis != 0) {
1635
                                throw new BeanException("Invalid parenthesis");
1636
                        }
151 erico 1637
 
163 erico 1638
                        applyRegex();
1639
 
151 erico 1640
                        if (AnsiSQLBeanSession.DEBUG) {
1641
                                System.out.println("CUSTOM QUERY: "+sb.toString());
1642
                        }
1643
 
1644
                        return sb.toString();
1645
                }
1646
 
1647
                public List<Object> getParamValues() {
1648
                        return paramValues;
1649
                }
1650
 
1651
        }
1652
 
149 erico 1653
}