MentaBean

Rev

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

<
Rev Author Line No. Line
2 soliveira 1
/*
22 soliveira 2
 * This program is free software: you can redistribute it and/or modify
3
 * it under the terms of the GNU General Public License as published by
4
 * the Free Software Foundation, either version 3 of the License, or
5
 * (at your option) any later version.
6
 *
7
 * This program is distributed in the hope that it will be useful,
2 soliveira 8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 soliveira 9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
14
 *
15
 * MentaBean => http://www.mentabean.org
16
 * Author: Sergio Oliveira Jr. (sergio.oliveira.jr@gmail.com)
2 soliveira 17
 */
18
package org.mentabean.jdbc;
19
 
20
import java.lang.reflect.Field;
21
import java.lang.reflect.Method;
22
import java.sql.Connection;
23
import java.sql.PreparedStatement;
24
import java.sql.ResultSet;
143 erico 25
import java.sql.SQLException;
2 soliveira 26
import java.util.ArrayList;
27
import java.util.HashMap;
28
import java.util.IdentityHashMap;
29
import java.util.Iterator;
30
import java.util.LinkedList;
31
import java.util.List;
32
import java.util.Map;
61 soliveira 33
import java.util.Set;
2 soliveira 34
 
35
import org.mentabean.BeanConfig;
36
import org.mentabean.BeanException;
37
import org.mentabean.BeanManager;
38
import org.mentabean.BeanSession;
39
import org.mentabean.DBField;
40
import org.mentabean.DBType;
149 erico 41
import org.mentabean.event.TriggerDispatcher;
42
import org.mentabean.event.TriggerDispatcher.Type;
43
import org.mentabean.event.TriggerEvent;
44
import org.mentabean.event.TriggerListener;
167 soliveira 45
import org.mentabean.sql.TableAlias;
2 soliveira 46
import org.mentabean.type.AutoIncrementType;
47
import org.mentabean.type.AutoTimestampType;
63 soliveira 48
import org.mentabean.type.NowOnInsertAndUpdateTimestampType;
56 soliveira 49
import org.mentabean.type.NowOnInsertTimestampType;
50
import org.mentabean.type.NowOnUpdateTimestampType;
61 soliveira 51
import org.mentabean.type.SizedType;
2 soliveira 52
import org.mentabean.util.InjectionUtils;
126 soliveira 53
import org.mentabean.util.Limit;
125 soliveira 54
import org.mentabean.util.OrderBy;
123 soliveira 55
import org.mentabean.util.PropertiesProxy;
90 soliveira 56
import org.mentabean.util.SQLUtils;
2 soliveira 57
 
11 soliveira 58
/**
59
 * The bean session implementation based on JDBC and SQL.
60
 *
61
 * @author soliveira
62
 */
54 soliveira 63
public class AnsiSQLBeanSession implements BeanSession {
2 soliveira 64
 
11 soliveira 65
        protected static boolean DEBUG = false;
149 erico 66
 
67
        protected static boolean DEBUG_NATIVE = false;
2 soliveira 68
 
47 soliveira 69
        /* The loaded map will be cleared when the session dies */
2 soliveira 70
        protected IdentityHashMap<Object, Map<String, Value>> loaded = new IdentityHashMap<Object, Map<String, Value>>();
47 soliveira 71
 
2 soliveira 72
        protected Connection conn;
47 soliveira 73
 
2 soliveira 74
        protected final BeanManager beanManager;
149 erico 75
 
76
        protected final TriggerDispatcher dispatcher = new TriggerDispatcher();
2 soliveira 77
 
11 soliveira 78
        /**
79
         * Creates a JdbcBeanSession with a BeanManager and a Connection.
80
         *
81
         * @param beanManager
82
         *            The bean manager
83
         * @param conn
84
         *            The database connection
85
         */
54 soliveira 86
        public AnsiSQLBeanSession(final BeanManager beanManager, final Connection conn) {
2 soliveira 87
                this.beanManager = beanManager;
88
                this.conn = conn;
89
        }
90
 
11 soliveira 91
        /**
92
         * Turn SQL debugging on and off.
93
         *
94
         * @param b
95
         *            true if it should be debugged
96
         */
97
        public static void debugSql(boolean b) {
149 erico 98
                DEBUG = b;
11 soliveira 99
        }
149 erico 100
 
101
        /**
102
         * Turn SQL native queries debugging on and off.
103
         *
104
         * @param b
105
         *            true if it should be debugged
106
         */
107
        public static void debugNativeSql(boolean b) {
108
                DEBUG_NATIVE = b;
109
        }
11 soliveira 110
 
111
        /**
112
         * Get the connection associated with this JdbcBeanSession.
113
         *
114
         * @return the database connection
115
         */
54 soliveira 116
        @Override
2 soliveira 117
        public Connection getConnection() {
118
 
119
                return conn;
120
        }
121
 
11 soliveira 122
        /**
22 soliveira 123
         * Get the command representing 'now' in this database. This base implementation returns null, in other words, no now command will be used.
11 soliveira 124
         *
125
         * @return the command for now in this database (now(), sysdate, etc)
126
         */
56 soliveira 127
        protected String getCurrentTimestampCommand() {
2 soliveira 128
 
129
                return null;
130
        }
131
 
12 soliveira 132
        /**
133
         * Get a value from a bean through reflection.
134
         *
135
         * @param bean
136
         * @param fieldName
13 soliveira 137
         * @return The value of a bean property
12 soliveira 138
         */
169 erico 139
        protected Object getValueFromBean(final Object bean, final String fieldName) {
2 soliveira 140
 
141
                return getValueFromBean(bean, fieldName, null);
142
 
143
        }
126 soliveira 144
 
128 soliveira 145
        public static String[] getProperties(Object[] names) {
126 soliveira 146
                if (names != null) {
147
                for(Object o : names) {
148
                        if (o instanceof String) {
149
                                PropertiesProxy.addPropertyName((String) o);
150
                        }
151
                }
152
                }
153
 
154
                if (PropertiesProxy.hasProperties()) {
155
                        return PropertiesProxy.getPropertyNames();
156
                } else {
157
                        return null;
158
                }
159
        }
160
 
12 soliveira 161
        /**
162
         * Get a value from a bean through reflection.
163
         *
164
         * @param bean
165
         * @param fieldName
166
         * @param m
13 soliveira 167
         * @return The value of a bean property
12 soliveira 168
         */
169 erico 169
        protected Object getValueFromBean(final Object bean, final String fieldName, Method m) {
89 soliveira 170
 
171
                int index;
172
 
173
                if ((index = fieldName.lastIndexOf(".")) > 0) {
174
 
175
                        String chain = fieldName.substring(0, index);
176
 
177
                        String lastField = fieldName.substring(index + 1);
178
 
179
                        Object deepestBean = getDeepestBean(bean, chain, false);
180
 
181
                        if (deepestBean == null) return null;
182
 
183
                        return getValueFromBean(deepestBean, lastField, m);
184
                }
185 erico 185
 
186
                if (bean == null) {
187
                        return null;
188
                }
2 soliveira 189
 
190
                if (m == null) {
191
                        m = InjectionUtils.findMethodToGet(bean.getClass(), fieldName);
192
                }
193
 
194
                if (m == null) {
103 erico 195
                        throw new BeanException("Cannot find method to get field from bean: " +
196
                                        "Class: " + bean.getClass() + ", field: " + fieldName);
2 soliveira 197
                }
198
 
199
                Object value = null;
200
 
4 soliveira 201
                try {
2 soliveira 202
 
4 soliveira 203
                        value = m.invoke(bean, (Object[]) null);
204
 
205
                        return value;
206
 
207
                } catch (Exception e) {
208
                        throw new BeanException(e);
209
                }
2 soliveira 210
        }
211
 
12 soliveira 212
        private static void checkPK(final Object value, final DBField dbField) {
2 soliveira 213
 
214
                if (value == null) {
215
                        throw new BeanException("pk is missing: " + dbField);
216
                } else if (value instanceof Number) {
217
 
218
                        final Number n = (Number) value;
219
 
220
                        if (n.doubleValue() <= 0) {
221
                                throw new BeanException("Number pk is missing: " + dbField);
222
                        }
223
                }
224
        }
89 soliveira 225
 
2 soliveira 226
        @Override
114 soliveira 227
        public boolean load(Object bean) {
228
                return loadImpl(bean, null, null);
229
        }
230
 
231
        @Override
126 soliveira 232
        public boolean load(Object bean, Object... properties) {
123 soliveira 233
 
126 soliveira 234
                return loadImpl(bean, getProperties(properties), null);
114 soliveira 235
        }
236
 
123 soliveira 237
 
238
        @Override
126 soliveira 239
        public boolean loadMinus(Object bean, Object... minus) {
123 soliveira 240
 
126 soliveira 241
                return loadImpl(bean, null, getProperties(minus));
242
 
114 soliveira 243
        }
244
 
245
        protected boolean loadImpl(final Object bean, String[] properties, String[] minus) {
2 soliveira 246
 
149 erico 247
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 248
 
249
                if (bc == null) {
250
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
251
                }
252
 
253
                if (bc.getNumberOfFields() == 0) {
254
                        throw new BeanException("BeanConfig has zero fields: " + bc);
255
                }
256
 
257
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
258
 
259
                sb.append("SELECT ");
260
 
261
                Iterator<DBField> iter = bc.fields();
262
 
263
                int count = 0;
264
 
265
                while (iter.hasNext()) {
266
 
116 soliveira 267
                        DBField field = iter.next();
114 soliveira 268
 
116 soliveira 269
                        final String fieldName = field.getDbName();
270
 
271
                        if (!field.isPK()) { // always load the PK...
272
 
143 erico 273
                        if (properties != null && !checkArray(fieldName, properties, bc)) {
116 soliveira 274
                                continue;
275
                        }
276
 
143 erico 277
                        if (minus != null && checkArray(fieldName, minus, bc)) {
116 soliveira 278
                                continue;
279
                        }
114 soliveira 280
                        }
2 soliveira 281
 
282
                        if (count++ > 0) {
283
                                sb.append(',');
284
                        }
285
 
286
                        sb.append(fieldName);
287
 
288
                }
289
 
290
                sb.append(" FROM ").append(bc.getTableName()).append(" WHERE ");
291
 
292
                if (!bc.hasPK()) {
293
                        throw new BeanException("Cannot load bean without a PK!");
294
                }
295
 
296
                iter = bc.pks();
297
 
298
                count = 0;
299
 
300
                final List<Value> values = new LinkedList<Value>();
301
 
302
                while (iter.hasNext()) {
303
 
304
                        final DBField dbField = iter.next();
305
 
306
                        final String fieldName = dbField.getName();
307
 
308
                        final String dbFieldName = dbField.getDbName();
309
 
310
                        final Object value = getValueFromBean(bean, fieldName);
311
 
312
                        checkPK(value, dbField);
313
 
314
                        if (count++ > 0) {
315
                                sb.append(" AND ");
316
                        }
317
 
318
                        sb.append(dbFieldName).append("=?");
319
 
320
                        values.add(new Value(dbField, value));
321
 
322
                }
323
 
324
                if (values.isEmpty()) {
325
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
326
                }
327
 
328
                if (conn == null) {
329
                        throw new BeanException("Connection is null!");
330
                }
331
 
332
                PreparedStatement stmt = null;
333
 
334
                ResultSet rset = null;
335
 
336
                try {
149 erico 337
 
2 soliveira 338
                        if (DEBUG) {
339
                                System.out.println("LOAD SQL: " + sb.toString());
340
                        }
149 erico 341
 
2 soliveira 342
                        stmt = conn.prepareStatement(sb.toString());
343
 
344
                        final Iterator<Value> iter2 = values.iterator();
345
 
346
                        int index = 0;
347
 
348
                        while (iter2.hasNext()) {
349
 
350
                                final Value v = iter2.next();
79 soliveira 351
 
2 soliveira 352
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
353
 
354
                        }
355
 
356
                        rset = stmt.executeQuery();
149 erico 357
 
358
                        if (DEBUG_NATIVE) {
359
                                System.out.println("LOAD SQL (NATIVE): " + stmt);
360
                        }
2 soliveira 361
 
362
                        index = 0;
363
 
364
                        final Map<String, Value> fieldsLoaded = new HashMap<String, Value>();
365
 
366
                        if (rset.next()) {
367
 
368
                                iter = bc.fields();
369
 
370
                                while (iter.hasNext()) {
371
 
372
                                        final DBField f = iter.next();
373
 
374
                                        final String fieldName = f.getName();
114 soliveira 375
 
116 soliveira 376
                                        if (!f.isPK()) {
377
 
143 erico 378
                                        if (properties != null && !checkArray(fieldName, properties, bc)) {
116 soliveira 379
                                                continue;
380
                                        }
381
 
143 erico 382
                                        if (minus != null && checkArray(fieldName, minus, bc)) {
116 soliveira 383
                                                continue;
384
                                        }
114 soliveira 385
                                        }
2 soliveira 386
 
387
                                        final DBType type = f.getType();
388
 
389
                                        final Object value = type.getFromResultSet(rset, ++index);
390
 
391
                                        injectValue(bean, fieldName, value, type.getTypeClass());
392
 
393
                                        fieldsLoaded.put(fieldName, new Value(f, value));
394
                                }
395
 
396
                        } else {
397
                                return false;
398
                        }
399
 
400
                        if (rset.next()) {
401
                                throw new BeanException("Load returned more than one row!");
402
                        }
403
 
404
                        loaded.put(bean, fieldsLoaded);
405
 
406
                        return true;
407
 
4 soliveira 408
                } catch (Exception e) {
409
 
410
                        throw new BeanException(e);
411
 
2 soliveira 412
                } finally {
413
 
11 soliveira 414
                        close(stmt, rset);
2 soliveira 415
                }
416
        }
89 soliveira 417
 
169 erico 418
        private Object getDeepestBean(Object target, String name, boolean create) {
89 soliveira 419
 
420
                int index;
421
 
422
                if ((index = name.indexOf('.')) > 0) {
423
 
424
                        String fieldName = name.substring(0, index);
2 soliveira 425
 
89 soliveira 426
                        String remainingName = name.substring(index + 1);
427
 
428
                        Object bean = getPropertyBean(target, fieldName, create);
429
 
430
                        return getDeepestBean(bean, remainingName, create);
431
                }
432
 
433
                return getPropertyBean(target, name, create);
434
        }
435
 
155 erico 436
        /**
437
         * Get a value from target through reflection and tries to create a new instance if create parameter is true
438
         * @param target
439
         * @param name
440
         * @param create
161 erico 441
         * @return The value from bean
155 erico 442
         */
169 erico 443
        protected Object getPropertyBean(Object target, String name, boolean create) {
89 soliveira 444
 
445
                Object value = getValueFromBean(target, name);
446
 
447
                if (value == null && create) {
448
 
449
                        // try to instantiate, must have a default constructor!
450
 
451
                        Class<?> beanClass = InjectionUtils.findPropertyType(target.getClass(), name);
452
 
453
                        if (beanClass == null) {
454
                                throw new BeanException("Cannot find property type: " + target.getClass() + " " + name);
455
                        }
456
 
457
                        try {
458
 
459
                                value = beanClass.newInstance();
460
 
461
                        } catch(Exception e) {
169 erico 462
 
463
                                value = getAbstractValue(target.getClass(), name);
89 soliveira 464
                        }
465
 
466
                        // don't forget to inject in the target so next time it is there...
467
 
468
                        injectValue(target, name, value, beanClass);
469
                }
470
 
471
                return value;
472
        }
169 erico 473
 
474
        private Object getAbstractValue(Class<? extends Object> clazz, String name) {
475
 
476
                try {
477
 
478
                        BeanConfig bc = getConfigFor(clazz);
479
                        if (bc != null) {
480
 
481
                                Class<? extends Object> instanceClass = bc.getAbstractProperty(name);
482
                                if (instanceClass != null) {
483
                                        return instanceClass.newInstance();
484
                                }
485
                        }
486
 
487
                        throw new BeanException("Cannot instantiate property name: " + name + " from "+clazz);                                 
488
 
489
                } catch (Exception e) {
490
                        throw new BeanException("Cannot instantiate abstract value for "+clazz+" (field "+name+")", e);
491
                }
492
        }
89 soliveira 493
 
12 soliveira 494
        /**
495
         * Inject a value in a bean through reflection.
496
         *
497
         * @param bean
498
         * @param fieldName
499
         * @param value
500
         * @param valueType
501
         */
169 erico 502
        protected void injectValue(final Object bean, final String fieldName, Object value, final Class<? extends Object> valueType) {
89 soliveira 503
 
504
                // first check if we have a chain of fields...
505
 
506
                int index;
507
 
508
                if ((index = fieldName.lastIndexOf(".")) > 0) {
509
 
97 soliveira 510
                        if (value == null) {
511
                                // there is nothing to do here, as we don't want to create any object since the id is null...
512
                                return;
513
                        }
514
 
89 soliveira 515
                        String chain = fieldName.substring(0, index);
516
 
517
                        String lastField = fieldName.substring(index + 1);
518
 
519
                        Object deepestBean = getDeepestBean(bean, chain, true);
520
 
521
                        injectValue(deepestBean, lastField, value, valueType);
522
 
523
                        return;
524
 
525
                }
2 soliveira 526
 
527
                final Method m = InjectionUtils.findMethodToInject(bean.getClass(), fieldName, value == null ? valueType : value.getClass());
528
 
529
                if (m == null) {
530
 
531
                        // try field...
532
 
533
                        final Field field = InjectionUtils.findFieldToInject(bean.getClass(), fieldName, value == null ? valueType : value.getClass());
97 soliveira 534
 
2 soliveira 535
                        if (field != null) {
97 soliveira 536
 
537
                                // if field is a primitive (not a wrapper or void), convert a null to its default value
538
                                if (field.getType().isPrimitive() && value == null) {
539
                                        value = InjectionUtils.getDefaultValueForPrimitive(field.getType());
540
                                }
541
 
2 soliveira 542
                                try {
543
 
544
                                        field.set(bean, value);
545
 
546
                                } catch (final Exception e) {
547
 
548
                                        e.printStackTrace();
549
 
550
                                        throw new BeanException(e);
551
                                }
97 soliveira 552
 
2 soliveira 553
                        } else {
79 soliveira 554
 
555
                                // if Long and can be expressed as integer, try integer...
556
                                if (value instanceof Long) {
557
                                        Long l = (Long) value;
558
                                        if (l.longValue() <= Integer.MAX_VALUE && l.longValue() >= Integer.MIN_VALUE) {
97 soliveira 559
                                                injectValue(bean, fieldName, l.intValue(), Integer.class); // recursion...
79 soliveira 560
                                                return;
561
                                        }
562
                                }
563
 
149 erico 564
                                // Field can be a GenericType (Object). If value is null nothing will be injected..
565
                                if (value != null)
566
                                        throw new BeanException("Cannot find field or method to inject: " + bean + " / " + fieldName);
2 soliveira 567
                        }
568
 
569
                } else {
97 soliveira 570
 
571
                        // if field is a primitive (not a wrapper or void), convert a null to its default value
572
                        Class<?> paramType = m.getParameterTypes()[0];
573
                        if (paramType.isPrimitive() && value == null) {
574
                                value = InjectionUtils.getDefaultValueForPrimitive(paramType);
575
                        }
576
 
2 soliveira 577
                        try {
97 soliveira 578
 
2 soliveira 579
                                m.invoke(bean, value);
580
 
581
                        } catch (final Exception e) {
582
 
583
                                e.printStackTrace();
584
 
585
                                throw new BeanException(e);
586
                        }
587
                }
588
        }
589
 
12 soliveira 590
        /**
22 soliveira 591
         * Some databases will sort before applying the limit (MySql), others will not (Oracle). Handle each one accordingly.
12 soliveira 592
         *
593
         * Note: This base implementation does nothing.
594
         *
595
         * @param sb
596
         * @param orderBy
597
         * @param limit
22 soliveira 598
         * @return A string builder with the the SQL modified for the limit operation
12 soliveira 599
         */
126 soliveira 600
        protected StringBuilder handleLimit(final StringBuilder sb, final OrderBy orderBy, final Limit limit) {
2 soliveira 601
 
602
                return sb;
603
        }
604
 
11 soliveira 605
        /**
22 soliveira 606
         * Build the column/field list for a SQL SELECT statement based on the bean configuration. Very useful to create select statements.
11 soliveira 607
         *
608
         * @param beanClass
609
         *            the bean class
610
         * @return the column/field list for a select
611
         */
32 soliveira 612
        @Override
2 soliveira 613
        public String buildSelect(final Class<? extends Object> beanClass) {
614
 
152 erico 615
                return buildSelectImpl(beanClass, null, null, null, true, true);
2 soliveira 616
        }
47 soliveira 617
 
32 soliveira 618
        @Override
126 soliveira 619
        public String buildSelect(final Class<? extends Object> beanClass, Object... properties) {
620
 
152 erico 621
                return buildSelectImpl(beanClass, null, getProperties(properties), null, true, true);
32 soliveira 622
        }
2 soliveira 623
 
11 soliveira 624
        /**
22 soliveira 625
         * Build a column/field list for a SQL SELECT statement based on the bean configuration. A table prefix will be used on each field. Very useful to create select statements on multiple tables (joins).
11 soliveira 626
         *
627
         * @param beanClass
628
         *            the bean class
629
         * @param tablePrefix
630
         *            the table prefix to use before each field
631
         * @return the column/field list for a select
632
         */
32 soliveira 633
        @Override
2 soliveira 634
        public String buildSelect(final Class<? extends Object> beanClass, final String tablePrefix) {
635
 
152 erico 636
                return buildSelectImpl(beanClass, tablePrefix, null, null, true, true);
32 soliveira 637
        }
2 soliveira 638
 
32 soliveira 639
        @Override
126 soliveira 640
        public String buildSelect(final Class<? extends Object> beanClass, final String tablePrefix, Object... properties) {
641
 
152 erico 642
                return buildSelectImpl(beanClass, tablePrefix, getProperties(properties), null, true, true);
2 soliveira 643
        }
47 soliveira 644
 
11 soliveira 645
        /**
22 soliveira 646
         * Like buildSelect but you can exclude some properties from the resulting list. Useful when you have a bean with too many properties and you just want to fetch a few.
11 soliveira 647
         *
22 soliveira 648
         * Note: The list of properties to exclude contains 'property names' and NOT database column names.
11 soliveira 649
         *
650
         * @param beanClass
651
         *            the bean class
652
         * @param minus
653
         *            a list for property names to exclude
654
         * @return the column/field list for a select
655
         */
32 soliveira 656
        @Override
126 soliveira 657
        public String buildSelectMinus(final Class<? extends Object> beanClass, final Object... minus) {
658
 
152 erico 659
                return buildSelectImpl(beanClass, null, null, getProperties(minus), true, true);
2 soliveira 660
        }
661
 
11 soliveira 662
        /**
22 soliveira 663
         * Same as buildSelectMinus with support for a database table prefix that will be applied on each field.
11 soliveira 664
         *
665
         * @param beanClass
666
         *            the bean class
667
         * @param tablePrefix
668
         *            the database table prefix
669
         * @param minus
670
         *            a list of property names to exclude
671
         * @return the column/field list for a select
672
         */
32 soliveira 673
        @Override
126 soliveira 674
        public String buildSelectMinus(final Class<? extends Object> beanClass, final String tablePrefix, final Object... minus) {
675
 
152 erico 676
                return buildSelectImpl(beanClass, tablePrefix, null, getProperties(minus), true, true);
2 soliveira 677
        }
678
 
152 erico 679
        protected String buildSelectImpl(final Class<? extends Object> beanClass, final String tablePrefix,
680
                        final String[] properties, final String[] minus, final boolean includePK, final boolean addSuffix) {
2 soliveira 681
 
149 erico 682
                final BeanConfig bc = getConfigFor(beanClass);
2 soliveira 683
 
684
                if (bc == null) {
685
                        return null;
686
                }
687
 
688
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
689
 
690
                final Iterator<DBField> iter = bc.fields();
691
 
692
                int count = 0;
693
 
694
                while (iter.hasNext()) {
695
 
696
                        final DBField field = iter.next();
697
 
698
                        final String dbField = field.getDbName();
699
 
116 soliveira 700
                        final String name = field.getName();
2 soliveira 701
 
150 erico 702
                        if (!field.isPK() || !includePK) { // always include PK
116 soliveira 703
 
143 erico 704
                        if (properties != null && !checkArray(name, properties, bc)) {
116 soliveira 705
                                continue;
706
                        }
707
 
143 erico 708
                        if (minus != null && checkArray(name, minus, bc)) {
116 soliveira 709
                                continue;
710
                        }
2 soliveira 711
                        }
712
 
713
                        if (count++ > 0) {
714
                                sb.append(",");
715
                        }
716
 
717
                        if (tablePrefix != null) {
718
 
152 erico 719
                                sb.append(tablePrefix).append('.').append(dbField);
2 soliveira 720
 
152 erico 721
                                if (addSuffix) {
722
 
723
                                        sb.append(' ');
2 soliveira 724
 
152 erico 725
                                        sb.append(tablePrefix).append('_').append(dbField);
726
                                }
2 soliveira 727
 
728
                        } else {
729
                                sb.append(dbField);
730
                        }
731
                }
732
 
733
                return sb.toString();
734
 
735
        }
47 soliveira 736
 
149 erico 737
        private boolean checkArray(final String value, final String[] array, final BeanConfig bc) {
2 soliveira 738
 
143 erico 739
                String column = propertyToColumn(bc, value);
2 soliveira 740
                for (int i = 0; i < array.length; i++) {
143 erico 741
                        if (propertyToColumn(bc, array[i]).equals(column)) {
2 soliveira 742
                                return true;
743
                        }
744
                }
745
 
746
                return false;
747
        }
748
 
12 soliveira 749
        /**
22 soliveira 750
         * Populate a bean (insert all its properties) from the results in a result set, based on the bean configuration.
12 soliveira 751
         *
752
         * @param rset
753
         *            the result set from where to get the property values
754
         * @param bean
755
         *            the bean to be populated
756
         * @throws Exception
757
         */
32 soliveira 758
        @Override
135 soliveira 759
        public void populateBean(final ResultSet rset, final Object bean) {
2 soliveira 760
 
150 erico 761
                populateBeanImpl(rset, bean, null, null, null, true);
2 soliveira 762
        }
47 soliveira 763
 
32 soliveira 764
        @Override
135 soliveira 765
        public void populateBean(final ResultSet rset, final Object bean, Object... properties) {
126 soliveira 766
 
150 erico 767
                populateBeanImpl(rset, bean, null, getProperties(properties), null, true);
32 soliveira 768
        }
2 soliveira 769
 
12 soliveira 770
        /**
22 soliveira 771
         * Same as populateBean, but use a table prefix before fetching the values from the result set. Useful when there are multiple tables involved and you want to avoid field name clashing.
12 soliveira 772
         *
773
         * @param rset
774
         *            the result set
775
         * @param bean
776
         *            the bean to be populated
777
         * @param tablePrefix
778
         *            the table prefix
779
         */
32 soliveira 780
        @Override
135 soliveira 781
        public void populateBean(final ResultSet rset, final Object bean, final String tablePrefix) {
2 soliveira 782
 
150 erico 783
                populateBeanImpl(rset, bean, tablePrefix, null, null, true);
2 soliveira 784
        }
785
 
32 soliveira 786
        @Override
135 soliveira 787
        public void populateBean(final ResultSet rset, final Object bean, final String tablePrefix, Object... properties) {
126 soliveira 788
 
150 erico 789
                populateBeanImpl(rset, bean, tablePrefix, getProperties(properties), null, true);
32 soliveira 790
        }
791
 
12 soliveira 792
        /**
793
         * Same as populateBean, but exclude some fields when populating.
794
         *
795
         * @param rset
796
         * @param bean
797
         * @param minus
798
         */
32 soliveira 799
        @Override
135 soliveira 800
        public void populateBeanMinus(final ResultSet rset, final Object bean, final Object... minus) {
126 soliveira 801
 
150 erico 802
                populateBeanImpl(rset, bean, null, null, getProperties(minus), true);
2 soliveira 803
        }
804
 
12 soliveira 805
        /**
22 soliveira 806
         * Same as populateBean, but exclude some fields when populating and use a table prefix in front of the field names.
12 soliveira 807
         *
808
         * @param rset
809
         * @param bean
810
         * @param tablePrefix
811
         * @param minus
812
         */
32 soliveira 813
        @Override
126 soliveira 814
        public void populateBeanMinus(final ResultSet rset, final Object bean, final String tablePrefix, final Object... minus) {
815
 
150 erico 816
                populateBeanImpl(rset, bean, tablePrefix, null, getProperties(minus), true);
2 soliveira 817
        }
818
 
150 erico 819
        protected void populateBeanImpl(final ResultSet rset, final Object bean, final String tablePrefix, final String[] properties, final String[] minus, boolean includePK) {
2 soliveira 820
 
149 erico 821
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 822
 
823
                if (bc == null) {
824
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
825
                }
826
 
827
                final Iterator<DBField> iter = bc.fields();
828
 
829
                final StringBuilder sbField = new StringBuilder(32);
830
 
831
                while (iter.hasNext()) {
832
 
833
                        final DBField f = iter.next();
834
 
835
                        final String fieldName = f.getName();
116 soliveira 836
 
150 erico 837
                        if (!f.isPK() || !includePK) { // always populate PK
116 soliveira 838
 
143 erico 839
                        if (properties != null && !checkArray(fieldName, properties, bc)) {
116 soliveira 840
                                continue;
841
                        }
842
 
143 erico 843
                        if (minus != null && checkArray(fieldName, minus, bc)) {
116 soliveira 844
                                continue;
845
                        }
2 soliveira 846
                        }
847
 
848
                        final String dbFieldName = f.getDbName();
849
 
850
                        final DBType type = f.getType();
851
 
852
                        sbField.setLength(0);
853
 
854
                        if (tablePrefix != null) {
855
                                sbField.append(tablePrefix).append('_').append(dbFieldName);
856
                        } else {
857
                                sbField.append(dbFieldName);
858
                        }
859
 
12 soliveira 860
                        try {
2 soliveira 861
 
12 soliveira 862
                                final Object value = type.getFromResultSet(rset, sbField.toString());
2 soliveira 863
 
12 soliveira 864
                                injectValue(bean, fieldName, value, type.getTypeClass());
865
 
866
                        } catch (Exception e) {
867
 
868
                                throw new BeanException(e);
869
                        }
2 soliveira 870
                }
871
        }
872
 
12 soliveira 873
        /**
874
         * Load a list of beans, but exclude some fields.
875
         *
876
         * @param <E>
877
         * @param bean
878
         * @param minus
879
         * @param orderBy
880
         * @param limit
13 soliveira 881
         * @return A list of beans
12 soliveira 882
         */
32 soliveira 883
        @Override
126 soliveira 884
        public <E> List<E> loadListMinus(final E bean, final OrderBy orderBy, final Limit limit, final Object... minus) {
885
 
886
                return loadListImpl(bean, orderBy, limit, null, getProperties(minus));
2 soliveira 887
        }
888
 
4 soliveira 889
        private <E> E checkUnique(final List<E> list) {
2 soliveira 890
 
891
                if (list == null || list.size() == 0) {
892
                        return null;
893
                } else if (list.size() > 1) {
894
                        throw new BeanException("Query returned more than one bean!");
895
                } else {
896
                        return list.get(0);
897
                }
898
        }
899
 
900
        @Override
126 soliveira 901
        public <E> List<E> loadList(final E bean, final OrderBy orderBy, final Limit limit) {
2 soliveira 902
 
32 soliveira 903
                return loadListImpl(bean, orderBy, limit, null, null);
2 soliveira 904
        }
47 soliveira 905
 
32 soliveira 906
        @Override
126 soliveira 907
        public <E> List<E> loadList(final E bean, final OrderBy orderBy, final Limit limit, Object... properties) {
908
 
909
                return loadListImpl(bean, orderBy, limit, getProperties(properties), null);
32 soliveira 910
        }
47 soliveira 911
 
126 soliveira 912
        private <E> StringBuilder prepareListQuery(StringBuilder sb, BeanConfig bc, E bean, OrderBy orderBy, Limit limit, List<Value> values) {
2 soliveira 913
 
19 soliveira 914
                sb.append(" FROM ").append(bc.getTableName()).append(" ");
915
 
916
                Iterator<DBField> iter = bc.fields();
917
 
918
                int count = 0;
919
 
920
                while (iter.hasNext()) {
921
 
922
                        final DBField field = iter.next();
923
 
924
                        final String dbField = field.getDbName();
925
 
89 soliveira 926
                        final Method m = findMethodToGet(bean, field.getName());
91 soliveira 927
 
928
                        boolean isNestedProperty = field.getName().contains(".");
19 soliveira 929
 
930
                        if (m == null) {
91 soliveira 931
                                if (!isNestedProperty) {
932
                                        throw new BeanException("Cannot find method to get field from bean: " + field.getName());
933
                                } else {
934
                                        continue; // nested property not set!
935
                                }
19 soliveira 936
                        }
937
 
938
                        final Class<? extends Object> returnType = m.getReturnType();
939
 
940
                        final Object value = getValueFromBean(bean, field.getName(), m);
941
 
942
                        if (!isSet(value, returnType)) {
943
                                continue;
944
                        }
945
 
946
                        if (count++ > 0) {
947
                                sb.append(" AND ");
948
                        } else {
949
                                sb.append(" WHERE ");
950
                        }
951
 
952
                        sb.append(dbField).append("=?");
953
 
954
                        values.add(new Value(field, value));
955
                }
956
 
143 erico 957
                sb.append(buildOrderBy(orderBy, bc));
958
 
959
                sb = handleLimit(sb, orderBy, limit);
960
 
961
                return sb;
962
        }
963
 
964
        private String buildOrderBy(OrderBy orderBy, BeanConfig bc) {
965
 
125 soliveira 966
                if (orderBy != null && !orderBy.isEmpty()) {
106 soliveira 967
 
125 soliveira 968
                        String orderByString = orderBy.toString();
106 soliveira 969
 
125 soliveira 970
                        String[] orders = orderByString.trim().split("\\s*,\\s*");
971
 
106 soliveira 972
                        for (String order : orders) {
973
                                if (order.contains(" ")) {
974
                                        order = order.substring(0, order.indexOf(" "));
975
                                }
125 soliveira 976
                                orderByString = orderByString.replace(order, propertyToColumn(bc, order));
106 soliveira 977
                        }
978
 
143 erico 979
                        StringBuilder sb = new StringBuilder();
125 soliveira 980
                        sb.append(" order by ").append(orderByString).append(" ");
143 erico 981
 
982
                        return sb.toString();
19 soliveira 983
                }
143 erico 984
                return " ";
19 soliveira 985
        }
106 soliveira 986
 
987
        /**
988
         * Returns a database column name for a bean attribute.
989
         * @param       bc - The <code>BeanConfig</code> object
143 erico 990
         * @param       property - A bean property
106 soliveira 991
         * @return      The database column name found if exists, otherwise will return the
143 erico 992
         * given bean <code>property</code>
106 soliveira 993
         */
149 erico 994
        public String propertyToColumn(BeanConfig bc, Object property) {
995
 
106 soliveira 996
                Iterator<DBField> it = bc.fields();
997
 
143 erico 998
                String propertyName = getProperties(new Object[] {property})[0];
999
 
106 soliveira 1000
                while (it.hasNext()) {
1001
                        DBField field = it.next();
1002
                        if (propertyName.equalsIgnoreCase(field.getName()))
1003
                                return field.getDbName();
1004
                }
1005
 
1006
                return propertyName;
1007
        }
143 erico 1008
 
1009
        @Override
1010
        public String propertyToColumn(Class<? extends Object> clazz, Object property) {
1011
 
1012
                return propertyToColumn(clazz, property, null);
1013
        }
1014
 
1015
        @Override
1016
        public String propertyToColumn(Class<? extends Object> clazz, Object property, String alias) {
1017
 
149 erico 1018
                BeanConfig bc = getConfigFor(clazz);
1019
 
143 erico 1020
                if (alias == null)
1021
                        return propertyToColumn(bc, property);
1022
 
1023
                return alias+"."+propertyToColumn(bc, property);
1024
        }
1025
 
1026
        @Override
1027
        public String buildTableName(Class<? extends Object> clazz) {
1028
 
149 erico 1029
                return getConfigFor(clazz).getTableName();
143 erico 1030
        }
149 erico 1031
 
1032
        @Override
1033
        public QueryBuilder buildQuery() {
19 soliveira 1034
 
149 erico 1035
                return new QueryBuilder(this);
1036
        }
1037
 
19 soliveira 1038
        @Override
1039
        public int countList(Object bean) {
127 soliveira 1040
                return countListImpl(bean, null, null);
19 soliveira 1041
        }
1042
 
127 soliveira 1043
        private int countListImpl(final Object bean, final OrderBy orderBy, final Limit limit) {
19 soliveira 1044
 
126 soliveira 1045
                if (limit != null && limit.intValue() == 0) {
19 soliveira 1046
                        return 0;
2 soliveira 1047
                }
1048
 
149 erico 1049
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 1050
 
1051
                if (bc == null) {
1052
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
1053
                }
1054
 
1055
                StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
1056
 
19 soliveira 1057
                sb.append("SELECT count(1)");
2 soliveira 1058
 
19 soliveira 1059
                final List<Value> values = new LinkedList<Value>();
2 soliveira 1060
 
19 soliveira 1061
                sb = prepareListQuery(sb, bc, bean, orderBy, limit, values);
2 soliveira 1062
 
19 soliveira 1063
                PreparedStatement stmt = null;
2 soliveira 1064
 
19 soliveira 1065
                ResultSet rset = null;
2 soliveira 1066
 
19 soliveira 1067
                try {
2 soliveira 1068
 
19 soliveira 1069
                        final String sql = sb.toString();
2 soliveira 1070
 
19 soliveira 1071
                        if (DEBUG) {
1072
                                System.out.println("COUNT LIST: " + sql);
2 soliveira 1073
                        }
1074
 
19 soliveira 1075
                        stmt = conn.prepareStatement(sql);
1076
 
1077
                        final Iterator<Value> iter2 = values.iterator();
1078
 
1079
                        int index = 0;
1080
 
1081
                        while (iter2.hasNext()) {
1082
 
1083
                                final Value v = iter2.next();
1084
 
1085
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
1086
 
2 soliveira 1087
                        }
1088
 
19 soliveira 1089
                        rset = stmt.executeQuery();
149 erico 1090
 
1091
                        if (DEBUG_NATIVE) {
1092
                                System.out.println("COUNT LIST (NATIVE): "+stmt);
1093
                        }
19 soliveira 1094
 
1095
                        rset.next();
1096
 
1097
                        return rset.getInt(1);
1098
 
1099
                } catch (Exception e) {
1100
 
1101
                        throw new BeanException(e);
1102
 
1103
                } finally {
1104
 
1105
                        close(stmt, rset);
2 soliveira 1106
                }
19 soliveira 1107
        }
2 soliveira 1108
 
126 soliveira 1109
        private <E> List<E> loadListImpl(final E bean, final OrderBy orderBy, final Limit limit, final String[] properties, final String[] minus) {
2 soliveira 1110
 
126 soliveira 1111
                if (limit != null && limit.intValue() == 0) {
19 soliveira 1112
                        return new ArrayList<E>();
1113
                }
2 soliveira 1114
 
149 erico 1115
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 1116
 
19 soliveira 1117
                if (bc == null) {
1118
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
1119
                }
2 soliveira 1120
 
19 soliveira 1121
                StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
1122
 
1123
                Iterator<DBField> iter = bc.fields();
1124
 
1125
                sb.append("SELECT ");
1126
 
1127
                int count = 0;
1128
 
2 soliveira 1129
                while (iter.hasNext()) {
1130
 
1131
                        final DBField field = iter.next();
1132
 
1133
                        final String dbField = field.getDbName();
1134
 
19 soliveira 1135
                        final String name = field.getName();
47 soliveira 1136
 
116 soliveira 1137
                        if (!field.isPK()) {
1138
 
143 erico 1139
                        if (properties != null && !checkArray(name, properties, bc)) {
116 soliveira 1140
                                continue;
1141
                        }
1142
 
143 erico 1143
                        if (minus != null && checkArray(name, minus, bc)) {
116 soliveira 1144
                                continue;
1145
                        }
32 soliveira 1146
                        }
2 soliveira 1147
 
1148
                        if (count++ > 0) {
19 soliveira 1149
                                sb.append(",");
2 soliveira 1150
                        }
1151
 
19 soliveira 1152
                        sb.append(dbField);
2 soliveira 1153
                }
1154
 
19 soliveira 1155
                final List<Value> values = new LinkedList<Value>();
2 soliveira 1156
 
19 soliveira 1157
                sb = prepareListQuery(sb, bc, bean, orderBy, limit, values);
2 soliveira 1158
 
1159
                PreparedStatement stmt = null;
1160
 
1161
                ResultSet rset = null;
1162
 
1163
                try {
1164
 
1165
                        final String sql = sb.toString();
149 erico 1166
 
2 soliveira 1167
                        if (DEBUG) {
149 erico 1168
                                System.out.println("LOAD LIST: "+sql);
2 soliveira 1169
                        }
149 erico 1170
 
2 soliveira 1171
                        stmt = conn.prepareStatement(sql);
1172
 
1173
                        final Iterator<Value> iter2 = values.iterator();
1174
 
1175
                        int index = 0;
1176
 
1177
                        while (iter2.hasNext()) {
1178
 
1179
                                final Value v = iter2.next();
1180
 
1181
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
1182
 
1183
                        }
1184
 
1185
                        rset = stmt.executeQuery();
149 erico 1186
 
1187
                        if (DEBUG_NATIVE) {
1188
                                System.out.println("LOAD LIST (NATIVE): " + stmt);
1189
                        }
2 soliveira 1190
 
1191
                        final List<E> results = new LinkedList<E>();
1192
 
1193
                        final Class<? extends Object> beanKlass = bean.getClass();
1194
 
1195
                        int total = 0;
1196
 
1197
                        while (rset.next()) {
1198
 
1199
                                iter = bc.fields();
1200
 
1201
                                index = 0;
1202
 
1203
                                final E item = (E) beanKlass.newInstance(); // not sure how to
1204
                                                                                                                        // handle generics
1205
                                                                                                                        // here...
1206
 
1207
                                while (iter.hasNext()) {
1208
 
1209
                                        final DBField f = iter.next();
1210
 
1211
                                        final String fieldName = f.getName();
116 soliveira 1212
 
1213
                                        if (!f.isPK()) {
2 soliveira 1214
 
143 erico 1215
                                        if (properties != null && !checkArray(fieldName, properties, bc)) {
116 soliveira 1216
                                                continue;
1217
                                        }
1218
 
143 erico 1219
                                        if (minus != null && checkArray(fieldName, minus, bc)) {
116 soliveira 1220
                                                continue;
1221
                                        }
2 soliveira 1222
                                        }
47 soliveira 1223
 
2 soliveira 1224
                                        final DBType type = f.getType();
1225
 
1226
                                        final Object value = type.getFromResultSet(rset, ++index);
1227
 
1228
                                        injectValue(item, fieldName, value, type.getTypeClass());
1229
                                }
1230
 
1231
                                results.add(item);
1232
 
1233
                                total++;
1234
 
126 soliveira 1235
                                if (limit != null && limit.intValue() > 0 && total == limit.intValue()) {
2 soliveira 1236
                                        return results;
1237
                                }
1238
                        }
1239
 
1240
                        return results;
1241
 
4 soliveira 1242
                } catch (Exception e) {
1243
 
1244
                        throw new BeanException(e);
1245
 
2 soliveira 1246
                } finally {
1247
 
11 soliveira 1248
                        close(stmt, rset);
2 soliveira 1249
                }
1250
        }
1251
 
12 soliveira 1252
        /**
1253
         * if Boolean consider TRUE to be set and FALSE to be not set.
1254
         *
1255
         * if Character, cast to integer and assume it is set if different than 0
1256
         *
1257
         * if Number consider everything different than zero to be set.
1258
         *
22 soliveira 1259
         * Otherwise returns TRUE for anything different than null and FALSE for null.
12 soliveira 1260
         *
1261
         * @param value
1262
         * @param returnType
13 soliveira 1263
         * @return true if is set
12 soliveira 1264
         */
2 soliveira 1265
        protected boolean isSet(final Object value, final Class<? extends Object> returnType) {
1266
 
1267
                if (value != null) {
1268
                        if (returnType.equals(boolean.class) && value instanceof Boolean) {
1269
 
1270
                                // if Boolean consider TRUE to be set and FALSE to be not set
1271
                                // (false = default value)
1272
 
1273
                                final boolean b = ((Boolean) value).booleanValue();
1274
 
1275
                                return b;
1276
 
1277
                        } else if (returnType.equals(char.class) && value instanceof Character) {
1278
 
1279
                                // if Character, cast to int and assume set if different than
1280
                                // 0...
1281
 
1282
                                final int c = ((Character) value).charValue();
1283
 
1284
                                return c != 0;
1285
 
1286
                        } else if (returnType.isPrimitive() && !returnType.equals(boolean.class) && !returnType.equals(char.class) && value instanceof Number) {
1287
 
1288
                                // if number consider everything different than zero to be
1289
                                // set...
1290
 
1291
                                final Number n = (Number) value;
1292
 
1293
                                if (n.intValue() != 0) {
1294
                                        return true;
1295
                                }
1296
 
1297
                        } else {
1298
                                return true;
1299
                        }
1300
                }
1301
 
1302
                return false;
1303
        }
1304
 
1305
        @Override
144 erico 1306
        public int update(final Object bean, Object... forceNull) {
2 soliveira 1307
 
144 erico 1308
                return update(bean, true, getProperties(forceNull));
2 soliveira 1309
        }
19 soliveira 1310
 
2 soliveira 1311
        @Override
17 soliveira 1312
        public int updateAll(final Object bean) {
19 soliveira 1313
 
144 erico 1314
                return update(bean, false, null);
17 soliveira 1315
        }
2 soliveira 1316
 
144 erico 1317
        private int update(final Object bean, final boolean dynUpdate, String[] nullProps) {
17 soliveira 1318
 
2 soliveira 1319
                final Map<String, Value> fieldsLoaded = loaded.get(bean);
1320
 
149 erico 1321
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 1322
 
1323
                if (bc == null) {
1324
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
1325
                }
1326
 
1327
                if (bc.getNumberOfFields() == 0) {
1328
                        throw new BeanException("BeanConfig has zero fields: " + bc);
1329
                }
1330
 
1331
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
1332
 
1333
                sb.append("UPDATE ").append(bc.getTableName()).append(" SET ");
1334
 
1335
                Iterator<DBField> iter = bc.fields();
1336
 
1337
                int count = 0;
1338
 
1339
                final List<Value> values = new LinkedList<Value>();
144 erico 1340
 
2 soliveira 1341
                while (iter.hasNext()) {
1342
 
1343
                        final DBField dbField = iter.next();
1344
 
1345
                        if (dbField.isPK()) {
1346
                                continue;
1347
                        }
1348
 
1349
                        final DBType type = dbField.getType();
1350
 
1351
                        if (type instanceof AutoIncrementType) {
1352
                                continue;
1353
                        }
1354
 
1355
                        if (type instanceof AutoTimestampType) {
1356
                                continue;
1357
                        }
61 soliveira 1358
 
63 soliveira 1359
                        boolean isNowOnUpdate = type instanceof NowOnUpdateTimestampType || type instanceof NowOnInsertAndUpdateTimestampType;
2 soliveira 1360
 
1361
                        final String fieldName = dbField.getName();
1362
 
1363
                        final String dbFieldName = dbField.getDbName();
61 soliveira 1364
 
55 soliveira 1365
                        if (!isNowOnUpdate) {
89 soliveira 1366
 
1367
                                Method m = findMethodToGet(bean, fieldName);
1368
 
91 soliveira 1369
                                Object value = null;
1370
                                Class<? extends Object> returnType = null;
1371
 
1372
                                boolean isNestedProperty = fieldName.contains(".");
1373
 
1374
                                if (m == null && !isNestedProperty) {
61 soliveira 1375
                                        throw new BeanException("Cannot find method to get field from bean: " + fieldName);
1376
                                }
1377
 
91 soliveira 1378
                                if (m != null) {
1379
                                        returnType = m.getReturnType();
1380
                                        value = getValueFromBean(bean, fieldName, m);
1381
                                }
61 soliveira 1382
 
1383
                                boolean update = false;
1384
 
1385
                                if (!dynUpdate) {
1386
 
1387
                                        // if this is NOT a dynUpdate then update all properties with
1388
                                        // whatever value they have
1389
 
1390
                                        update = true;
1391
 
1392
                                } else if (fieldsLoaded != null) {
1393
 
1394
                                        // this is a dynUpdate, check if value is dirty, in other words,
1395
                                        // if it has changed since it was loaded...
1396
 
1397
                                        final Value v = fieldsLoaded.get(fieldName);
1398
 
1399
                                        if (v != null) {
1400
                                                if (value == null && v.value != null) {
1401
                                                        update = true;
1402
                                                } else if (value != null && v.value == null) {
1403
                                                        update = true;
1404
                                                } else if (value == null && v.value == null) {
1405
                                                        update = false;
1406
                                                } else {
1407
                                                        update = !value.equals(v.value);
1408
                                                }
1409
                                        }
1410
 
1411
                                } else {
1412
 
1413
                                        // this is a dynUpdate, but bean was not previously loaded from
1414
                                        // the database...
1415
                                        // in this case only update if the property is considered to be
1416
                                        // SET...
1417
 
1418
                                        update = isSet(value, returnType);
144 erico 1419
 
1420
                                        if (!update && nullProps != null) {
184 erico 1421
 
144 erico 1422
                                                update = checkArray(fieldName, nullProps, bc);
184 erico 1423
 
1424
                                                //null in database column
1425
                                                value = null;
144 erico 1426
                                        }
61 soliveira 1427
                                }
1428
 
1429
                                if (update) {
1430
 
1431
                                        if (count++ > 0) {
1432
                                                sb.append(',');
1433
                                        }
1434
 
1435
                                        sb.append(dbFieldName).append("=?");
1436
 
1437
                                        values.add(new Value(dbField, value));
1438
 
1439
                                }
1440
 
2 soliveira 1441
                        } else {
61 soliveira 1442
 
2 soliveira 1443
                                if (count++ > 0) {
1444
                                        sb.append(',');
1445
                                }
97 soliveira 1446
 
55 soliveira 1447
                                sb.append(dbFieldName).append("=");
61 soliveira 1448
 
56 soliveira 1449
                                String nowCommand = getCurrentTimestampCommand();
61 soliveira 1450
 
56 soliveira 1451
                                if (nowCommand == null) {
61 soliveira 1452
 
107 soliveira 1453
                                        sb.append("?");
61 soliveira 1454
 
1455
                                        values.add(new Value(dbField, new java.util.Date()));
1456
 
56 soliveira 1457
                                } else {
61 soliveira 1458
 
56 soliveira 1459
                                        sb.append(nowCommand);
1460
                                }
2 soliveira 1461
                        }
1462
                }
1463
 
1464
                if (count == 0) {
17 soliveira 1465
                        return 0;
2 soliveira 1466
                }
1467
 
1468
                sb.append(" WHERE ");
1469
 
1470
                if (!bc.hasPK()) {
1471
                        throw new BeanException("Cannot update bean without a PK!");
1472
                }
1473
 
1474
                iter = bc.pks();
1475
 
1476
                count = 0;
1477
 
1478
                while (iter.hasNext()) {
1479
 
1480
                        final DBField dbField = iter.next();
1481
 
1482
                        final String fieldName = dbField.getName();
1483
 
1484
                        final String dbFieldName = dbField.getDbName();
1485
 
1486
                        final Object value = getValueFromBean(bean, fieldName);
1487
 
1488
                        if (value == null) {
1489
                                throw new BeanException("pk is missing: " + dbField);
1490
                        } else if (value instanceof Number) {
1491
 
1492
                                final Number n = (Number) value;
1493
 
1494
                                if (n.doubleValue() <= 0) {
1495
                                        throw new BeanException("Number pk is missing: " + dbField);
1496
                                }
1497
 
1498
                        }
1499
 
1500
                        if (count++ > 0) {
1501
                                sb.append(" AND ");
1502
                        }
1503
 
1504
                        sb.append(dbFieldName).append("=?");
1505
 
1506
                        values.add(new Value(dbField, value));
1507
 
1508
                }
1509
 
1510
                if (values.isEmpty()) {
1511
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
1512
                }
1513
 
1514
                if (conn == null) {
1515
                        throw new BeanException("Connection is null!");
1516
                }
1517
 
1518
                PreparedStatement stmt = null;
1519
 
1520
                try {
1521
 
1522
                        if (DEBUG) {
1523
                                System.out.println("UPDATE SQL: " + sb.toString());
1524
                        }
1525
 
149 erico 1526
                        dispatchBeforeUpdate(bean);
1527
 
2 soliveira 1528
                        stmt = conn.prepareStatement(sb.toString());
1529
 
1530
                        Iterator<Value> iter2 = values.iterator();
1531
 
1532
                        int index = 0;
1533
 
1534
                        while (iter2.hasNext()) {
1535
 
1536
                                final Value v = iter2.next();
1537
 
1538
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
1539
 
1540
                        }
1541
 
1542
                        final int x = stmt.executeUpdate();
149 erico 1543
 
1544
                        if (DEBUG_NATIVE) {
1545
                                System.out.println("UPDATE SQL (NATIVE): " + stmt);
1546
                        }
2 soliveira 1547
 
1548
                        if (x > 1) {
1549
                                throw new BeanException("update modified more than one line: " + x);
1550
                        }
1551
 
1552
                        if (x == 0) {
1553
                                return 0;
1554
                        }
1555
 
1556
                        if (fieldsLoaded != null) {
1557
 
1558
                                iter2 = values.iterator();
1559
 
1560
                                while (iter2.hasNext()) {
1561
 
1562
                                        final Value v = iter2.next();
1563
 
1564
                                        if (v.field.isPK()) {
1565
                                                continue;
1566
                                        }
1567
 
1568
                                        final Value vv = fieldsLoaded.get(v.field.getName());
1569
 
1570
                                        if (vv != null) {
1571
                                                vv.value = v.value;
1572
                                        }
1573
                                }
1574
                        }
149 erico 1575
 
1576
                        dispatchAfterUpdate(bean);
2 soliveira 1577
 
1578
                        return 1;
1579
 
4 soliveira 1580
                } catch (Exception e) {
1581
 
1582
                        throw new BeanException(e);
1583
 
2 soliveira 1584
                } finally {
1585
 
11 soliveira 1586
                        close(stmt);
2 soliveira 1587
                }
1588
        }
89 soliveira 1589
 
149 erico 1590
        @Override
1591
        public <E> E createBasicInstance(E bean) {
1592
 
1593
                try {
1594
                        BeanConfig bc = getConfigFor(bean.getClass());
1595
 
1596
                        Iterator<DBField> pks = bc.pks();
1597
                        DBField pk = null;
1598
                        Object value = null;
1599
                        E basic = (E) bean.getClass().newInstance();
1600
 
1601
                        while (pks.hasNext()) {
1602
 
1603
                                pk = pks.next();
1604
 
1605
                                value = getValueFromBean(bean, pk.getName());
1606
 
1607
                                checkPK(value, pk);
1608
 
1609
                                injectValue(basic, pk.getName(), value, null);
1610
                        }
1611
 
1612
                        return basic;
1613
 
1614
                }catch(Exception e) {
1615
                        throw new BeanException(e);
1616
                }
1617
        }
1618
 
152 erico 1619
        @Override
195 erico 1620
        public <E> int updateDiff(E newBean, E oldBean) {
1621
 
1622
                List<String> nullProps = new LinkedList<String>();
1623
 
1624
                E diff = compareDifferences(newBean, oldBean, nullProps);
1625
 
1626
                return diff == null ? 0 : update(diff, nullProps.toArray());
1627
        }
1628
 
1629
        @Override
1630
        public <E> E compareDifferences(E bean, E another, List<String> nullProps) {
1631
 
1632
                try {
1633
                        BeanConfig bc = getConfigFor(bean.getClass());
1634
 
1635
                        Iterator<DBField> fields = bc.fields();
1636
                        DBField field;
1637
                        Object valueBean, valueAnother;
1638
 
1639
                        E diff = (E) bean.getClass().newInstance();
1640
 
1641
                        boolean hasDiff = false;
1642
 
1643
                        while (fields.hasNext()) {
1644
 
1645
                                field = fields.next();
1646
 
198 erico 1647
                                Method m = findMethodToGet(bean, field.getName());
195 erico 1648
 
1649
                                boolean isNestedProperty = field.getName().contains(".");
1650
 
1651
                                if (m == null) {
1652
                                        if (!isNestedProperty) {
198 erico 1653
 
195 erico 1654
                                                throw new BeanException("Cannot find method to get field from bean: " + field.getName());
198 erico 1655
 
195 erico 1656
                                        } else {
198 erico 1657
 
1658
                                                int index = field.getName().lastIndexOf(".")+1;
1659
 
1660
                                                Object deepest = getDeepestBean(bean, field.getName().substring(0, index-1), true);
1661
 
1662
                                                String lastField = field.getName().substring(index);
1663
 
1664
                                                m = findMethodToGet(deepest, lastField);
195 erico 1665
                                        }
1666
                                }
1667
 
1668
                                final Class<? extends Object> returnType = m.getReturnType();
1669
 
198 erico 1670
                                valueBean = getValueFromBean(bean, field.getName());
195 erico 1671
 
1672
                                if (field.isPK()) {
1673
 
1674
                                        injectValue(diff, field.getName(), valueBean, null);
1675
 
1676
                                        continue;
1677
                                }
1678
 
198 erico 1679
                                valueAnother = getValueFromBean(another, field.getName());
195 erico 1680
 
1681
                                if (!isSet(valueBean, returnType) && !isSet(valueAnother, returnType)) {
1682
 
1683
                                        continue;
1684
                                }
1685
 
1686
                                if (!isSet(valueBean, returnType)) {
1687
 
1688
                                        nullProps.add(field.getName());
1689
 
1690
                                        hasDiff = true;
1691
 
1692
                                        continue;
1693
                                }
1694
 
1695
                                if (!valueBean.equals(valueAnother)) {
1696
 
1697
                                        hasDiff = true;
1698
 
1699
                                        injectValue(diff, field.getName(), valueBean, returnType);
1700
                                }
1701
 
1702
                        }
1703
 
1704
                        return hasDiff ? diff : null;
1705
 
1706
                }catch(Exception e) {
1707
                        throw new BeanException(e);
1708
                }
1709
        }
1710
 
1711
        @Override
152 erico 1712
        public int save(final Object bean, Object... forceNull) {
143 erico 1713
 
152 erico 1714
                return save(bean, true, getProperties(forceNull));
143 erico 1715
        }
1716
 
152 erico 1717
        @Override
143 erico 1718
        public int saveAll(final Object bean) {
1719
 
1720
                return save(bean, false);
1721
        }
1722
 
1723
        /**
1724
         * Update or insert a bean into database. It tries to update first and then insert
1725
         * @param       bean Object to update or insert
1726
         * @param       dynUpdate flag indicating a dynamic update
1727
         * @return      A value <b>0 (zero)</b> if operation was an <code>update</code>,
1728
         * <b>1 (one) if</b> <code>insert</code> method was executed
1729
         * @see #saveAll(Object)
161 erico 1730
         * @see #save(Object, Object...)
143 erico 1731
         */
191 erico 1732
        protected int save(Object bean, boolean dynUpdate, String nullProps[]) {
143 erico 1733
 
1734
                try {
1735
 
191 erico 1736
                        if (secureLoadUnique(bean) != null) {
149 erico 1737
 
152 erico 1738
                                update(bean, dynUpdate, nullProps);
143 erico 1739
 
148 erico 1740
                                return UPDATE;
191 erico 1741
 
1742
                        } else {
1743
 
1744
                                insert(bean);
1745
 
1746
                                return INSERT;
143 erico 1747
                        }
1748
 
1749
                }catch (BeanException e) {
1750
 
191 erico 1751
                        throw e;
143 erico 1752
                }
191 erico 1753
        }
1754
 
1755
        protected Object secureLoadUnique(Object bean) {
1756
 
143 erico 1757
                try {
1758
 
191 erico 1759
                        return loadUnique(createBasicInstance(bean));
1760
 
1761
                } catch (Exception e) {
1762
 
1763
                        return null;
143 erico 1764
                }
1765
        }
1766
 
169 erico 1767
        private Method findMethodToGet(Object bean, String fieldName) {
89 soliveira 1768
 
1769
                Method m = null;
1770
 
1771
                int index;
1772
 
1773
                if ((index = fieldName.lastIndexOf(".")) > 0) {
1774
 
1775
                        String chain = fieldName.substring(0, index);
1776
 
1777
                        String lastField = fieldName.substring(index + 1);
1778
 
185 erico 1779
                        Object deepestBean = getDeepestBean(bean, chain, false);
89 soliveira 1780
 
1781
                        if (deepestBean != null) {
1782
 
1783
                                m = InjectionUtils.findMethodToGet(deepestBean.getClass(), lastField);
1784
                        }
1785
 
1786
                } else {
2 soliveira 1787
 
89 soliveira 1788
                        m = InjectionUtils.findMethodToGet(bean.getClass(), fieldName);
1789
                }
1790
 
1791
                return m;
1792
        }
1793
 
48 soliveira 1794
        protected class QueryAndValues {
2 soliveira 1795
 
48 soliveira 1796
                public QueryAndValues(StringBuilder sb, List<Value> values) {
1797
                        this.sb = sb;
1798
                        this.values = values;
1799
                }
1800
 
1801
                public StringBuilder sb;
1802
                public List<Value> values;
1803
        }
1804
 
1805
        protected QueryAndValues prepareInsertQuery(Object bean) {
1806
 
149 erico 1807
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 1808
 
1809
                if (bc == null) {
1810
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
1811
                }
1812
 
1813
                if (bc.getNumberOfFields() == 0) {
1814
                        throw new BeanException("BeanConfig has zero fields: " + bc);
1815
                }
1816
 
1817
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
1818
 
1819
                sb.append("INSERT INTO ").append(bc.getTableName()).append("(");
1820
 
1821
                Iterator<DBField> iter = bc.pks();
1822
 
1823
                int count = 0;
1824
 
1825
                final List<Value> values = new LinkedList<Value>();
1826
 
1827
                while (iter.hasNext()) {
1828
 
1829
                        final DBField dbField = iter.next();
1830
 
1831
                        final String fieldName = dbField.getName();
1832
 
1833
                        final String dbFieldName = dbField.getDbName();
1834
 
1835
                        final DBType type = dbField.getType();
1836
 
1837
                        if (type instanceof AutoIncrementType) {
1838
                                continue;
1839
                        }
1840
 
1841
                        if (type instanceof AutoTimestampType) {
1842
                                continue;
1843
                        }
61 soliveira 1844
 
56 soliveira 1845
                        if (type instanceof NowOnUpdateTimestampType) {
1846
                                continue;
1847
                        }
63 soliveira 1848
 
2 soliveira 1849
                        final Object value = getValueFromBean(bean, fieldName);
1850
 
1851
                        if (count++ > 0) {
1852
                                sb.append(',');
1853
                        }
1854
 
1855
                        sb.append(dbFieldName);
1856
 
1857
                        values.add(new Value(dbField, value));
1858
                }
1859
 
1860
                iter = bc.fields();
1861
 
1862
                while (iter.hasNext()) {
1863
 
1864
                        final DBField dbField = iter.next();
1865
 
1866
                        if (dbField.isPK()) {
1867
                                continue;
1868
                        }
1869
 
1870
                        final String fieldName = dbField.getName();
1871
 
1872
                        final String dbFieldName = dbField.getDbName();
1873
 
1874
                        final DBType type = dbField.getType();
1875
 
1876
                        if (type instanceof AutoIncrementType) {
1877
                                continue;
1878
                        }
1879
 
1880
                        if (type instanceof AutoTimestampType) {
1881
                                continue;
1882
                        }
61 soliveira 1883
 
56 soliveira 1884
                        if (type instanceof NowOnUpdateTimestampType) {
1885
                                continue;
2 soliveira 1886
                        }
61 soliveira 1887
 
63 soliveira 1888
                        boolean isNowOnInsert = type instanceof NowOnInsertTimestampType || type instanceof NowOnInsertAndUpdateTimestampType;
61 soliveira 1889
 
56 soliveira 1890
                        if (!isNowOnInsert) {
2 soliveira 1891
 
61 soliveira 1892
                                Object value = getValueFromBean(bean, fieldName);
1893
 
1894
                                if (count++ > 0) {
1895
                                        sb.append(',');
1896
                                }
1897
 
1898
                                sb.append(dbFieldName);
1899
 
1900
                                values.add(new Value(dbField, value));
1901
 
56 soliveira 1902
                        } else {
61 soliveira 1903
 
56 soliveira 1904
                                if (count++ > 0) {
61 soliveira 1905
                                        sb.append(',');
1906
                                }
1907
 
1908
                                sb.append(dbFieldName);
1909
 
1910
                                String cmd = getCurrentTimestampCommand();
1911
 
1912
                                if (cmd == null) {
1913
                                        values.add(new Value(dbField, new java.util.Date()));
1914
                                } else {
1915
                                        values.add(new Value(dbField, true));
1916
                                }
2 soliveira 1917
                        }
1918
                }
1919
 
1920
                if (count == 0) {
1921
                        throw new BeanException("There is nothing to insert!");
1922
                }
1923
 
1924
                sb.append(") VALUES(");
1925
 
1926
                final Iterator<Value> valuesIter = values.iterator();
1927
 
1928
                int i = 0;
1929
 
1930
                while (valuesIter.hasNext()) {
1931
 
1932
                        final Value v = valuesIter.next();
1933
 
1934
                        if (i > 0) {
1935
                                sb.append(',');
1936
                        }
1937
 
56 soliveira 1938
                        if (v.isSysdate) {
1939
                                sb.append(getCurrentTimestampCommand());
2 soliveira 1940
                        } else {
1941
                                sb.append('?');
1942
                        }
1943
 
1944
                        i++;
1945
                }
1946
 
1947
                sb.append(')');
1948
 
1949
                if (values.isEmpty()) {
1950
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
1951
                }
1952
 
48 soliveira 1953
                return new QueryAndValues(sb, values);
1954
        }
2 soliveira 1955
 
48 soliveira 1956
        protected Map<String, Value> bindToInsertStatement(PreparedStatement stmt, List<Value> values) {
2 soliveira 1957
 
48 soliveira 1958
                final Iterator<Value> iter2 = values.iterator();
2 soliveira 1959
 
48 soliveira 1960
                int index = 0;
1961
 
1962
                final Map<String, Value> fieldsLoaded = new HashMap<String, Value>();
1963
 
1964
                while (iter2.hasNext()) {
1965
 
1966
                        final Value v = iter2.next();
1967
 
56 soliveira 1968
                        if (v.isSysdate && getCurrentTimestampCommand() != null) {
48 soliveira 1969
                                continue;
2 soliveira 1970
                        }
1971
 
48 soliveira 1972
                        try {
2 soliveira 1973
 
48 soliveira 1974
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
2 soliveira 1975
 
48 soliveira 1976
                        } catch (Exception e) {
1977
                                throw new BeanException(e);
1978
                        }
2 soliveira 1979
 
48 soliveira 1980
                        fieldsLoaded.put(v.field.getName(), v);
1981
                }
2 soliveira 1982
 
48 soliveira 1983
                return fieldsLoaded;
1984
        }
2 soliveira 1985
 
48 soliveira 1986
        @Override
1987
        public void insert(final Object bean) {
2 soliveira 1988
 
48 soliveira 1989
                QueryAndValues qav = prepareInsertQuery(bean);
2 soliveira 1990
 
48 soliveira 1991
                StringBuilder sb = qav.sb;
2 soliveira 1992
 
48 soliveira 1993
                List<Value> values = qav.values;
2 soliveira 1994
 
48 soliveira 1995
                if (conn == null) {
1996
                        throw new BeanException("Connection is null!");
1997
                }
1998
 
1999
                PreparedStatement stmt = null;
2000
 
2001
                try {
2002
 
2003
                        if (DEBUG) {
2004
                                System.out.println("INSERT SQL: " + sb.toString());
2 soliveira 2005
                        }
2006
 
48 soliveira 2007
                        stmt = conn.prepareStatement(sb.toString());
2008
 
2009
                        Map<String, Value> fieldsLoaded = bindToInsertStatement(stmt, values);
2010
 
149 erico 2011
                        dispatchBeforeInsert(bean);
2012
 
2 soliveira 2013
                        final int x = stmt.executeUpdate();
149 erico 2014
 
2015
                        if (DEBUG_NATIVE) {
2016
                                System.out.println("INSERT SQL (NATIVE): " + stmt);
2017
                        }
2 soliveira 2018
 
2019
                        if (x > 1) {
2020
                                throw new BeanException("insert modified more than one line: " + x);
2021
                        }
2022
 
2023
                        if (x == 0) {
2024
                                throw new BeanException("Nothing was inserted! Insert returned 0 rows!");
2025
                        }
149 erico 2026
 
2 soliveira 2027
                        loaded.put(bean, fieldsLoaded);
2028
 
4 soliveira 2029
                } catch (Exception e) {
2030
 
2031
                        throw new BeanException(e);
2032
 
2 soliveira 2033
                } finally {
11 soliveira 2034
                        close(stmt);
2 soliveira 2035
                }
2036
        }
192 erico 2037
 
2038
        @Override
2039
        public int deleteAll(final Object bean) {
2040
 
2041
                final BeanConfig bc = getConfigFor(bean.getClass());
2042
 
2043
                if (bc.getNumberOfFields() == 0) {
2044
                        throw new BeanException("BeanConfig has zero fields: " + bc);
2045
                }
2046
 
2047
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
2048
 
2049
                sb.append("DELETE ");
2050
 
2051
                List<Value> values = new LinkedList<Value>();
2052
 
2053
                prepareListQuery(sb, bc, bean, null, null, values);
2054
 
2055
                if (conn == null) {
2056
                        throw new BeanException("Connection is null!");
2057
                }
2 soliveira 2058
 
192 erico 2059
                PreparedStatement stmt = null;
2060
 
2061
                try {
2062
 
2063
                        if (DEBUG) {
2064
                                System.out.println("DELETE SQL: " + sb.toString());
2065
                        }
2066
 
2067
                        stmt = conn.prepareStatement(sb.toString());
2068
 
2069
                        final Iterator<Value> iter2 = values.iterator();
2070
 
2071
                        int index = 0;
2072
 
2073
                        while (iter2.hasNext()) {
2074
 
2075
                                final Value v = iter2.next();
2076
 
2077
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
2078
 
2079
                        }
2080
 
2081
                        dispatchBeforeDelete(bean);
2082
 
2083
                        final int x = stmt.executeUpdate();
2084
 
2085
                        if (DEBUG_NATIVE) {
2086
                                System.out.println("DELETE SQL (NATIVE): " + stmt);
2087
                        }
2088
 
2089
                        dispatchAfterDelete(bean);
2090
 
2091
                        return x;
2092
 
2093
                } catch (Exception e) {
2094
 
2095
                        throw new BeanException(e);
2096
 
2097
                } finally {
2098
 
2099
                        close(stmt);
2100
                }
2101
        }
2102
 
2 soliveira 2103
        @Override
4 soliveira 2104
        public boolean delete(final Object bean) {
2 soliveira 2105
 
149 erico 2106
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 2107
 
2108
                if (bc.getNumberOfFields() == 0) {
2109
                        throw new BeanException("BeanConfig has zero fields: " + bc);
2110
                }
2111
 
2112
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
2113
 
2114
                sb.append("DELETE FROM ").append(bc.getTableName()).append(" WHERE ");
2115
 
2116
                if (!bc.hasPK()) {
2117
                        throw new BeanException("Cannot delete bean without a PK!");
2118
                }
2119
 
2120
                final Iterator<DBField> iter = bc.pks();
2121
 
2122
                final List<Value> values = new LinkedList<Value>();
2123
 
2124
                int count = 0;
2125
 
2126
                while (iter.hasNext()) {
2127
 
2128
                        final DBField dbField = iter.next();
2129
 
2130
                        final String fieldName = dbField.getName();
2131
 
2132
                        final String dbFieldName = dbField.getDbName();
2133
 
2134
                        final Object value = getValueFromBean(bean, fieldName);
2135
 
2136
                        if (value == null) {
2137
                                throw new BeanException("pk is missing: " + dbField);
2138
                        } else if (value instanceof Number) {
2139
 
2140
                                final Number n = (Number) value;
2141
 
2142
                                if (n.doubleValue() <= 0) {
2143
                                        throw new BeanException("Number pk is missing: " + dbField);
2144
                                }
2145
 
2146
                        }
2147
 
2148
                        if (count++ > 0) {
2149
                                sb.append(" AND ");
2150
                        }
2151
 
2152
                        sb.append(dbFieldName).append("=?");
2153
 
2154
                        values.add(new Value(dbField, value));
2155
 
2156
                }
2157
 
2158
                if (values.isEmpty()) {
2159
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
2160
                }
2161
 
2162
                if (conn == null) {
2163
                        throw new BeanException("Connection is null!");
2164
                }
2165
 
2166
                PreparedStatement stmt = null;
2167
 
2168
                try {
2169
 
2170
                        if (DEBUG) {
2171
                                System.out.println("DELETE SQL: " + sb.toString());
2172
                        }
2173
 
2174
                        stmt = conn.prepareStatement(sb.toString());
2175
 
2176
                        final Iterator<Value> iter2 = values.iterator();
2177
 
2178
                        int index = 0;
2179
 
2180
                        while (iter2.hasNext()) {
2181
 
2182
                                final Value v = iter2.next();
2183
 
2184
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
2185
 
2186
                        }
2187
 
149 erico 2188
                        dispatchBeforeDelete(bean);
2189
 
2 soliveira 2190
                        final int x = stmt.executeUpdate();
149 erico 2191
 
2192
                        if (DEBUG_NATIVE) {
2193
                                System.out.println("DELETE SQL (NATIVE): " + stmt);
2194
                        }
2 soliveira 2195
 
2196
                        if (x > 1) {
2197
                                throw new BeanException("delete modified more than one line: " + x);
2198
                        }
2199
 
2200
                        if (x == 0) {
2201
                                return false;
2202
                        }
2203
 
2204
                        loaded.remove(bean);
2205
 
149 erico 2206
                        dispatchAfterDelete(bean);
2207
 
2 soliveira 2208
                        return true;
2209
 
4 soliveira 2210
                } catch (Exception e) {
2211
 
2212
                        throw new BeanException(e);
2213
 
2 soliveira 2214
                } finally {
2215
 
11 soliveira 2216
                        close(stmt);
2 soliveira 2217
                }
2218
        }
2219
 
2220
        @Override
4 soliveira 2221
        public <E> List<E> loadList(final E bean) {
2 soliveira 2222
 
126 soliveira 2223
                return loadListImpl(bean, null, null, null, null);
2 soliveira 2224
        }
47 soliveira 2225
 
32 soliveira 2226
        @Override
126 soliveira 2227
        public <E> List<E> loadList(final E bean, Object... properties) {
2228
 
2229
                return loadListImpl(bean, null, null, getProperties(properties), null);
32 soliveira 2230
        }
114 soliveira 2231
 
2 soliveira 2232
        @Override
114 soliveira 2233
        public <E> E loadUnique(E bean) {
2234
                return loadUniqueImpl(bean, null, null);
2235
        }
2236
 
2237
        @Override
126 soliveira 2238
        public <E> E loadUnique(E bean, Object... properties) {
2239
                return loadUniqueImpl(bean, getProperties(properties), null);
114 soliveira 2240
        }
2241
 
2242
        @Override
126 soliveira 2243
        public <E> E loadUniqueMinus(E bean, Object... minus) {
2244
                return loadUniqueImpl(bean, null, getProperties(minus));
123 soliveira 2245
        }
2 soliveira 2246
 
114 soliveira 2247
        protected <E> E loadUniqueImpl(final E bean, String[] properties, String[] minus) {
2248
 
126 soliveira 2249
                E o = checkUnique(loadListImpl(bean, null, new Limit(2), properties, minus));
64 soliveira 2250
 
2251
                if (o != null) {
2252
                        load(o); // load twice to attach to session so dynamic update is by default!
2253
                }
2254
 
2255
                return o;
2 soliveira 2256
        }
2257
 
2258
        @Override
125 soliveira 2259
        public <E> List<E> loadList(final E bean, final OrderBy orderBy) {
2 soliveira 2260
 
126 soliveira 2261
                return loadListImpl(bean, orderBy, null, null, null);
2 soliveira 2262
        }
47 soliveira 2263
 
32 soliveira 2264
        @Override
126 soliveira 2265
        public <E> List<E> loadList(final E bean, final OrderBy orderBy, Object... properties) {
2266
                return loadListImpl(bean, orderBy, null, getProperties(properties), null);
32 soliveira 2267
        }
2 soliveira 2268
 
2269
        @Override
126 soliveira 2270
        public <E> List<E> loadList(final E bean, final Limit limit) {
2 soliveira 2271
 
2272
                return loadList(bean, null, limit);
2273
        }
47 soliveira 2274
 
32 soliveira 2275
        @Override
126 soliveira 2276
        public <E> List<E> loadList(final E bean, final Limit limit, Object... properties) {
2277
                return loadListImpl(bean, null, limit, getProperties(properties), null);
32 soliveira 2278
        }
2 soliveira 2279
 
12 soliveira 2280
        /**
22 soliveira 2281
         * Load a list of beans, but exclude some fields. Useful when the bean has too many properties and you don't want to fetch everything from the database.
12 soliveira 2282
         *
2283
         * @param <E>
2284
         * @param bean
2285
         * @param minus
13 soliveira 2286
         * @return A list of beans
12 soliveira 2287
         */
32 soliveira 2288
        @Override
126 soliveira 2289
        public <E> List<E> loadListMinus(final E bean, final Object... minus) {
2290
 
2291
                return loadListMinus(bean, null, null, minus);
2 soliveira 2292
        }
2293
 
12 soliveira 2294
        /**
22 soliveira 2295
         * Load a list of beans, but exclude some fields. Useful when the bean has too many properties and you don't want to fetch everything from the database.
12 soliveira 2296
         *