MentaBean

Rev

Rev 213 | Rev 218 | 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
 
213 erico 1288
                                // if number consider everything different than zero to be set...
2 soliveira 1289
 
1290
                                final Number n = (Number) value;
1291
 
213 erico 1292
                                if (n.doubleValue() != 0d) {
2 soliveira 1293
                                        return true;
1294
                                }
1295
 
1296
                        } else {
1297
                                return true;
1298
                        }
1299
                }
1300
 
1301
                return false;
1302
        }
1303
 
1304
        @Override
144 erico 1305
        public int update(final Object bean, Object... forceNull) {
2 soliveira 1306
 
144 erico 1307
                return update(bean, true, getProperties(forceNull));
2 soliveira 1308
        }
19 soliveira 1309
 
2 soliveira 1310
        @Override
17 soliveira 1311
        public int updateAll(final Object bean) {
19 soliveira 1312
 
144 erico 1313
                return update(bean, false, null);
17 soliveira 1314
        }
2 soliveira 1315
 
144 erico 1316
        private int update(final Object bean, final boolean dynUpdate, String[] nullProps) {
17 soliveira 1317
 
2 soliveira 1318
                final Map<String, Value> fieldsLoaded = loaded.get(bean);
1319
 
149 erico 1320
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 1321
 
1322
                if (bc == null) {
1323
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
1324
                }
1325
 
1326
                if (bc.getNumberOfFields() == 0) {
1327
                        throw new BeanException("BeanConfig has zero fields: " + bc);
1328
                }
1329
 
1330
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
1331
 
1332
                sb.append("UPDATE ").append(bc.getTableName()).append(" SET ");
1333
 
1334
                Iterator<DBField> iter = bc.fields();
1335
 
1336
                int count = 0;
1337
 
1338
                final List<Value> values = new LinkedList<Value>();
144 erico 1339
 
2 soliveira 1340
                while (iter.hasNext()) {
1341
 
1342
                        final DBField dbField = iter.next();
1343
 
1344
                        if (dbField.isPK()) {
1345
                                continue;
1346
                        }
1347
 
1348
                        final DBType type = dbField.getType();
1349
 
1350
                        if (type instanceof AutoIncrementType) {
1351
                                continue;
1352
                        }
1353
 
1354
                        if (type instanceof AutoTimestampType) {
1355
                                continue;
1356
                        }
61 soliveira 1357
 
63 soliveira 1358
                        boolean isNowOnUpdate = type instanceof NowOnUpdateTimestampType || type instanceof NowOnInsertAndUpdateTimestampType;
2 soliveira 1359
 
1360
                        final String fieldName = dbField.getName();
1361
 
1362
                        final String dbFieldName = dbField.getDbName();
61 soliveira 1363
 
55 soliveira 1364
                        if (!isNowOnUpdate) {
89 soliveira 1365
 
1366
                                Method m = findMethodToGet(bean, fieldName);
1367
 
91 soliveira 1368
                                Object value = null;
1369
                                Class<? extends Object> returnType = null;
1370
 
1371
                                boolean isNestedProperty = fieldName.contains(".");
1372
 
1373
                                if (m == null && !isNestedProperty) {
61 soliveira 1374
                                        throw new BeanException("Cannot find method to get field from bean: " + fieldName);
1375
                                }
1376
 
91 soliveira 1377
                                if (m != null) {
1378
                                        returnType = m.getReturnType();
1379
                                        value = getValueFromBean(bean, fieldName, m);
1380
                                }
61 soliveira 1381
 
1382
                                boolean update = false;
1383
 
1384
                                if (!dynUpdate) {
1385
 
1386
                                        // if this is NOT a dynUpdate then update all properties with
1387
                                        // whatever value they have
1388
 
1389
                                        update = true;
1390
 
1391
                                } else if (fieldsLoaded != null) {
1392
 
1393
                                        // this is a dynUpdate, check if value is dirty, in other words,
1394
                                        // if it has changed since it was loaded...
1395
 
1396
                                        final Value v = fieldsLoaded.get(fieldName);
1397
 
1398
                                        if (v != null) {
1399
                                                if (value == null && v.value != null) {
1400
                                                        update = true;
1401
                                                } else if (value != null && v.value == null) {
1402
                                                        update = true;
1403
                                                } else if (value == null && v.value == null) {
1404
                                                        update = false;
1405
                                                } else {
1406
                                                        update = !value.equals(v.value);
1407
                                                }
1408
                                        }
1409
 
1410
                                } else {
1411
 
1412
                                        // this is a dynUpdate, but bean was not previously loaded from
1413
                                        // the database...
1414
                                        // in this case only update if the property is considered to be
1415
                                        // SET...
1416
 
1417
                                        update = isSet(value, returnType);
144 erico 1418
 
1419
                                        if (!update && nullProps != null) {
184 erico 1420
 
144 erico 1421
                                                update = checkArray(fieldName, nullProps, bc);
184 erico 1422
 
1423
                                                //null in database column
1424
                                                value = null;
144 erico 1425
                                        }
61 soliveira 1426
                                }
1427
 
1428
                                if (update) {
1429
 
1430
                                        if (count++ > 0) {
1431
                                                sb.append(',');
1432
                                        }
1433
 
1434
                                        sb.append(dbFieldName).append("=?");
1435
 
1436
                                        values.add(new Value(dbField, value));
1437
 
1438
                                }
1439
 
2 soliveira 1440
                        } else {
61 soliveira 1441
 
2 soliveira 1442
                                if (count++ > 0) {
1443
                                        sb.append(',');
1444
                                }
97 soliveira 1445
 
55 soliveira 1446
                                sb.append(dbFieldName).append("=");
61 soliveira 1447
 
56 soliveira 1448
                                String nowCommand = getCurrentTimestampCommand();
61 soliveira 1449
 
56 soliveira 1450
                                if (nowCommand == null) {
61 soliveira 1451
 
107 soliveira 1452
                                        sb.append("?");
61 soliveira 1453
 
1454
                                        values.add(new Value(dbField, new java.util.Date()));
1455
 
56 soliveira 1456
                                } else {
61 soliveira 1457
 
56 soliveira 1458
                                        sb.append(nowCommand);
1459
                                }
2 soliveira 1460
                        }
1461
                }
1462
 
1463
                if (count == 0) {
17 soliveira 1464
                        return 0;
2 soliveira 1465
                }
1466
 
1467
                sb.append(" WHERE ");
1468
 
1469
                if (!bc.hasPK()) {
1470
                        throw new BeanException("Cannot update bean without a PK!");
1471
                }
1472
 
1473
                iter = bc.pks();
1474
 
1475
                count = 0;
1476
 
1477
                while (iter.hasNext()) {
1478
 
1479
                        final DBField dbField = iter.next();
1480
 
1481
                        final String fieldName = dbField.getName();
1482
 
1483
                        final String dbFieldName = dbField.getDbName();
1484
 
1485
                        final Object value = getValueFromBean(bean, fieldName);
1486
 
1487
                        if (value == null) {
1488
                                throw new BeanException("pk is missing: " + dbField);
1489
                        } else if (value instanceof Number) {
1490
 
1491
                                final Number n = (Number) value;
1492
 
1493
                                if (n.doubleValue() <= 0) {
1494
                                        throw new BeanException("Number pk is missing: " + dbField);
1495
                                }
1496
 
1497
                        }
1498
 
1499
                        if (count++ > 0) {
1500
                                sb.append(" AND ");
1501
                        }
1502
 
1503
                        sb.append(dbFieldName).append("=?");
1504
 
1505
                        values.add(new Value(dbField, value));
1506
 
1507
                }
1508
 
1509
                if (values.isEmpty()) {
1510
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
1511
                }
1512
 
1513
                if (conn == null) {
1514
                        throw new BeanException("Connection is null!");
1515
                }
1516
 
1517
                PreparedStatement stmt = null;
1518
 
1519
                try {
1520
 
1521
                        if (DEBUG) {
1522
                                System.out.println("UPDATE SQL: " + sb.toString());
1523
                        }
1524
 
149 erico 1525
                        dispatchBeforeUpdate(bean);
1526
 
2 soliveira 1527
                        stmt = conn.prepareStatement(sb.toString());
1528
 
1529
                        Iterator<Value> iter2 = values.iterator();
1530
 
1531
                        int index = 0;
1532
 
1533
                        while (iter2.hasNext()) {
1534
 
1535
                                final Value v = iter2.next();
1536
 
1537
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
1538
 
1539
                        }
1540
 
1541
                        final int x = stmt.executeUpdate();
149 erico 1542
 
1543
                        if (DEBUG_NATIVE) {
1544
                                System.out.println("UPDATE SQL (NATIVE): " + stmt);
1545
                        }
2 soliveira 1546
 
1547
                        if (x > 1) {
1548
                                throw new BeanException("update modified more than one line: " + x);
1549
                        }
1550
 
1551
                        if (x == 0) {
1552
                                return 0;
1553
                        }
1554
 
1555
                        if (fieldsLoaded != null) {
1556
 
1557
                                iter2 = values.iterator();
1558
 
1559
                                while (iter2.hasNext()) {
1560
 
1561
                                        final Value v = iter2.next();
1562
 
1563
                                        if (v.field.isPK()) {
1564
                                                continue;
1565
                                        }
1566
 
1567
                                        final Value vv = fieldsLoaded.get(v.field.getName());
1568
 
1569
                                        if (vv != null) {
1570
                                                vv.value = v.value;
1571
                                        }
1572
                                }
1573
                        }
149 erico 1574
 
1575
                        dispatchAfterUpdate(bean);
2 soliveira 1576
 
1577
                        return 1;
1578
 
4 soliveira 1579
                } catch (Exception e) {
1580
 
1581
                        throw new BeanException(e);
1582
 
2 soliveira 1583
                } finally {
1584
 
11 soliveira 1585
                        close(stmt);
2 soliveira 1586
                }
1587
        }
89 soliveira 1588
 
149 erico 1589
        @Override
1590
        public <E> E createBasicInstance(E bean) {
1591
 
1592
                try {
1593
                        BeanConfig bc = getConfigFor(bean.getClass());
1594
 
1595
                        Iterator<DBField> pks = bc.pks();
1596
                        DBField pk = null;
1597
                        Object value = null;
1598
                        E basic = (E) bean.getClass().newInstance();
1599
 
1600
                        while (pks.hasNext()) {
1601
 
1602
                                pk = pks.next();
1603
 
1604
                                value = getValueFromBean(bean, pk.getName());
1605
 
1606
                                checkPK(value, pk);
1607
 
1608
                                injectValue(basic, pk.getName(), value, null);
1609
                        }
1610
 
1611
                        return basic;
1612
 
1613
                }catch(Exception e) {
1614
                        throw new BeanException(e);
1615
                }
1616
        }
1617
 
152 erico 1618
        @Override
195 erico 1619
        public <E> int updateDiff(E newBean, E oldBean) {
1620
 
1621
                List<String> nullProps = new LinkedList<String>();
1622
 
1623
                E diff = compareDifferences(newBean, oldBean, nullProps);
1624
 
1625
                return diff == null ? 0 : update(diff, nullProps.toArray());
1626
        }
1627
 
1628
        @Override
1629
        public <E> E compareDifferences(E bean, E another, List<String> nullProps) {
1630
 
1631
                try {
1632
                        BeanConfig bc = getConfigFor(bean.getClass());
1633
 
1634
                        Iterator<DBField> fields = bc.fields();
1635
                        DBField field;
1636
                        Object valueBean, valueAnother;
1637
 
1638
                        E diff = (E) bean.getClass().newInstance();
1639
 
1640
                        boolean hasDiff = false;
1641
 
1642
                        while (fields.hasNext()) {
1643
 
1644
                                field = fields.next();
1645
 
198 erico 1646
                                Method m = findMethodToGet(bean, field.getName());
195 erico 1647
 
1648
                                boolean isNestedProperty = field.getName().contains(".");
1649
 
1650
                                if (m == null) {
1651
                                        if (!isNestedProperty) {
198 erico 1652
 
195 erico 1653
                                                throw new BeanException("Cannot find method to get field from bean: " + field.getName());
198 erico 1654
 
195 erico 1655
                                        } else {
198 erico 1656
 
1657
                                                int index = field.getName().lastIndexOf(".")+1;
1658
 
1659
                                                Object deepest = getDeepestBean(bean, field.getName().substring(0, index-1), true);
1660
 
1661
                                                String lastField = field.getName().substring(index);
1662
 
1663
                                                m = findMethodToGet(deepest, lastField);
195 erico 1664
                                        }
1665
                                }
1666
 
1667
                                final Class<? extends Object> returnType = m.getReturnType();
1668
 
198 erico 1669
                                valueBean = getValueFromBean(bean, field.getName());
195 erico 1670
 
1671
                                if (field.isPK()) {
1672
 
1673
                                        injectValue(diff, field.getName(), valueBean, null);
1674
 
1675
                                        continue;
1676
                                }
1677
 
198 erico 1678
                                valueAnother = getValueFromBean(another, field.getName());
195 erico 1679
 
1680
                                if (!isSet(valueBean, returnType) && !isSet(valueAnother, returnType)) {
1681
 
1682
                                        continue;
1683
                                }
1684
 
1685
                                if (!isSet(valueBean, returnType)) {
1686
 
1687
                                        nullProps.add(field.getName());
1688
 
1689
                                        hasDiff = true;
1690
 
1691
                                        continue;
1692
                                }
1693
 
1694
                                if (!valueBean.equals(valueAnother)) {
1695
 
1696
                                        hasDiff = true;
1697
 
1698
                                        injectValue(diff, field.getName(), valueBean, returnType);
1699
                                }
1700
 
1701
                        }
1702
 
1703
                        return hasDiff ? diff : null;
1704
 
1705
                }catch(Exception e) {
1706
                        throw new BeanException(e);
1707
                }
1708
        }
1709
 
1710
        @Override
152 erico 1711
        public int save(final Object bean, Object... forceNull) {
143 erico 1712
 
152 erico 1713
                return save(bean, true, getProperties(forceNull));
143 erico 1714
        }
1715
 
152 erico 1716
        @Override
143 erico 1717
        public int saveAll(final Object bean) {
1718
 
1719
                return save(bean, false);
1720
        }
1721
 
1722
        /**
1723
         * Update or insert a bean into database. It tries to update first and then insert
1724
         * @param       bean Object to update or insert
1725
         * @param       dynUpdate flag indicating a dynamic update
1726
         * @return      A value <b>0 (zero)</b> if operation was an <code>update</code>,
1727
         * <b>1 (one) if</b> <code>insert</code> method was executed
1728
         * @see #saveAll(Object)
161 erico 1729
         * @see #save(Object, Object...)
143 erico 1730
         */
191 erico 1731
        protected int save(Object bean, boolean dynUpdate, String nullProps[]) {
143 erico 1732
 
1733
                try {
1734
 
191 erico 1735
                        if (secureLoadUnique(bean) != null) {
149 erico 1736
 
152 erico 1737
                                update(bean, dynUpdate, nullProps);
143 erico 1738
 
148 erico 1739
                                return UPDATE;
191 erico 1740
 
1741
                        } else {
1742
 
1743
                                insert(bean);
1744
 
1745
                                return INSERT;
143 erico 1746
                        }
1747
 
1748
                }catch (BeanException e) {
1749
 
191 erico 1750
                        throw e;
143 erico 1751
                }
191 erico 1752
        }
1753
 
1754
        protected Object secureLoadUnique(Object bean) {
1755
 
143 erico 1756
                try {
1757
 
191 erico 1758
                        return loadUnique(createBasicInstance(bean));
1759
 
1760
                } catch (Exception e) {
1761
 
1762
                        return null;
143 erico 1763
                }
1764
        }
1765
 
169 erico 1766
        private Method findMethodToGet(Object bean, String fieldName) {
89 soliveira 1767
 
1768
                Method m = null;
1769
 
1770
                int index;
1771
 
1772
                if ((index = fieldName.lastIndexOf(".")) > 0) {
1773
 
1774
                        String chain = fieldName.substring(0, index);
1775
 
1776
                        String lastField = fieldName.substring(index + 1);
1777
 
185 erico 1778
                        Object deepestBean = getDeepestBean(bean, chain, false);
89 soliveira 1779
 
1780
                        if (deepestBean != null) {
1781
 
1782
                                m = InjectionUtils.findMethodToGet(deepestBean.getClass(), lastField);
1783
                        }
1784
 
1785
                } else {
2 soliveira 1786
 
89 soliveira 1787
                        m = InjectionUtils.findMethodToGet(bean.getClass(), fieldName);
1788
                }
1789
 
1790
                return m;
1791
        }
1792
 
48 soliveira 1793
        protected class QueryAndValues {
2 soliveira 1794
 
48 soliveira 1795
                public QueryAndValues(StringBuilder sb, List<Value> values) {
1796
                        this.sb = sb;
1797
                        this.values = values;
1798
                }
1799
 
1800
                public StringBuilder sb;
1801
                public List<Value> values;
1802
        }
1803
 
1804
        protected QueryAndValues prepareInsertQuery(Object bean) {
1805
 
149 erico 1806
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 1807
 
1808
                if (bc == null) {
1809
                        throw new BeanException("Cannot find bean config: " + bean.getClass());
1810
                }
1811
 
1812
                if (bc.getNumberOfFields() == 0) {
1813
                        throw new BeanException("BeanConfig has zero fields: " + bc);
1814
                }
1815
 
1816
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
1817
 
1818
                sb.append("INSERT INTO ").append(bc.getTableName()).append("(");
1819
 
1820
                Iterator<DBField> iter = bc.pks();
1821
 
1822
                int count = 0;
1823
 
1824
                final List<Value> values = new LinkedList<Value>();
1825
 
1826
                while (iter.hasNext()) {
1827
 
1828
                        final DBField dbField = iter.next();
1829
 
1830
                        final String fieldName = dbField.getName();
1831
 
1832
                        final String dbFieldName = dbField.getDbName();
1833
 
1834
                        final DBType type = dbField.getType();
1835
 
1836
                        if (type instanceof AutoIncrementType) {
1837
                                continue;
1838
                        }
1839
 
1840
                        if (type instanceof AutoTimestampType) {
1841
                                continue;
1842
                        }
61 soliveira 1843
 
56 soliveira 1844
                        if (type instanceof NowOnUpdateTimestampType) {
1845
                                continue;
1846
                        }
63 soliveira 1847
 
2 soliveira 1848
                        final Object value = getValueFromBean(bean, fieldName);
1849
 
1850
                        if (count++ > 0) {
1851
                                sb.append(',');
1852
                        }
1853
 
1854
                        sb.append(dbFieldName);
1855
 
1856
                        values.add(new Value(dbField, value));
1857
                }
1858
 
1859
                iter = bc.fields();
1860
 
1861
                while (iter.hasNext()) {
1862
 
1863
                        final DBField dbField = iter.next();
1864
 
1865
                        if (dbField.isPK()) {
1866
                                continue;
1867
                        }
1868
 
1869
                        final String fieldName = dbField.getName();
1870
 
1871
                        final String dbFieldName = dbField.getDbName();
1872
 
1873
                        final DBType type = dbField.getType();
1874
 
1875
                        if (type instanceof AutoIncrementType) {
1876
                                continue;
1877
                        }
1878
 
1879
                        if (type instanceof AutoTimestampType) {
1880
                                continue;
1881
                        }
61 soliveira 1882
 
56 soliveira 1883
                        if (type instanceof NowOnUpdateTimestampType) {
1884
                                continue;
2 soliveira 1885
                        }
61 soliveira 1886
 
63 soliveira 1887
                        boolean isNowOnInsert = type instanceof NowOnInsertTimestampType || type instanceof NowOnInsertAndUpdateTimestampType;
61 soliveira 1888
 
56 soliveira 1889
                        if (!isNowOnInsert) {
2 soliveira 1890
 
61 soliveira 1891
                                Object value = getValueFromBean(bean, fieldName);
1892
 
1893
                                if (count++ > 0) {
1894
                                        sb.append(',');
1895
                                }
1896
 
1897
                                sb.append(dbFieldName);
1898
 
1899
                                values.add(new Value(dbField, value));
1900
 
56 soliveira 1901
                        } else {
61 soliveira 1902
 
56 soliveira 1903
                                if (count++ > 0) {
61 soliveira 1904
                                        sb.append(',');
1905
                                }
1906
 
1907
                                sb.append(dbFieldName);
1908
 
1909
                                String cmd = getCurrentTimestampCommand();
1910
 
1911
                                if (cmd == null) {
1912
                                        values.add(new Value(dbField, new java.util.Date()));
1913
                                } else {
1914
                                        values.add(new Value(dbField, true));
1915
                                }
2 soliveira 1916
                        }
1917
                }
1918
 
1919
                if (count == 0) {
1920
                        throw new BeanException("There is nothing to insert!");
1921
                }
1922
 
1923
                sb.append(") VALUES(");
1924
 
1925
                final Iterator<Value> valuesIter = values.iterator();
1926
 
1927
                int i = 0;
1928
 
1929
                while (valuesIter.hasNext()) {
1930
 
1931
                        final Value v = valuesIter.next();
1932
 
1933
                        if (i > 0) {
1934
                                sb.append(',');
1935
                        }
1936
 
56 soliveira 1937
                        if (v.isSysdate) {
1938
                                sb.append(getCurrentTimestampCommand());
2 soliveira 1939
                        } else {
1940
                                sb.append('?');
1941
                        }
1942
 
1943
                        i++;
1944
                }
1945
 
1946
                sb.append(')');
1947
 
1948
                if (values.isEmpty()) {
1949
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
1950
                }
1951
 
48 soliveira 1952
                return new QueryAndValues(sb, values);
1953
        }
2 soliveira 1954
 
48 soliveira 1955
        protected Map<String, Value> bindToInsertStatement(PreparedStatement stmt, List<Value> values) {
2 soliveira 1956
 
48 soliveira 1957
                final Iterator<Value> iter2 = values.iterator();
2 soliveira 1958
 
48 soliveira 1959
                int index = 0;
1960
 
1961
                final Map<String, Value> fieldsLoaded = new HashMap<String, Value>();
1962
 
1963
                while (iter2.hasNext()) {
1964
 
1965
                        final Value v = iter2.next();
1966
 
56 soliveira 1967
                        if (v.isSysdate && getCurrentTimestampCommand() != null) {
48 soliveira 1968
                                continue;
2 soliveira 1969
                        }
1970
 
48 soliveira 1971
                        try {
2 soliveira 1972
 
48 soliveira 1973
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
2 soliveira 1974
 
48 soliveira 1975
                        } catch (Exception e) {
1976
                                throw new BeanException(e);
1977
                        }
2 soliveira 1978
 
48 soliveira 1979
                        fieldsLoaded.put(v.field.getName(), v);
1980
                }
2 soliveira 1981
 
48 soliveira 1982
                return fieldsLoaded;
1983
        }
2 soliveira 1984
 
48 soliveira 1985
        @Override
1986
        public void insert(final Object bean) {
2 soliveira 1987
 
48 soliveira 1988
                QueryAndValues qav = prepareInsertQuery(bean);
2 soliveira 1989
 
48 soliveira 1990
                StringBuilder sb = qav.sb;
2 soliveira 1991
 
48 soliveira 1992
                List<Value> values = qav.values;
2 soliveira 1993
 
48 soliveira 1994
                if (conn == null) {
1995
                        throw new BeanException("Connection is null!");
1996
                }
1997
 
1998
                PreparedStatement stmt = null;
1999
 
2000
                try {
2001
 
2002
                        if (DEBUG) {
2003
                                System.out.println("INSERT SQL: " + sb.toString());
2 soliveira 2004
                        }
2005
 
48 soliveira 2006
                        stmt = conn.prepareStatement(sb.toString());
2007
 
2008
                        Map<String, Value> fieldsLoaded = bindToInsertStatement(stmt, values);
2009
 
149 erico 2010
                        dispatchBeforeInsert(bean);
2011
 
2 soliveira 2012
                        final int x = stmt.executeUpdate();
149 erico 2013
 
2014
                        if (DEBUG_NATIVE) {
2015
                                System.out.println("INSERT SQL (NATIVE): " + stmt);
2016
                        }
2 soliveira 2017
 
2018
                        if (x > 1) {
2019
                                throw new BeanException("insert modified more than one line: " + x);
2020
                        }
2021
 
2022
                        if (x == 0) {
2023
                                throw new BeanException("Nothing was inserted! Insert returned 0 rows!");
2024
                        }
149 erico 2025
 
2 soliveira 2026
                        loaded.put(bean, fieldsLoaded);
2027
 
4 soliveira 2028
                } catch (Exception e) {
2029
 
2030
                        throw new BeanException(e);
2031
 
2 soliveira 2032
                } finally {
11 soliveira 2033
                        close(stmt);
2 soliveira 2034
                }
2035
        }
192 erico 2036
 
2037
        @Override
2038
        public int deleteAll(final Object bean) {
2039
 
2040
                final BeanConfig bc = getConfigFor(bean.getClass());
2041
 
2042
                if (bc.getNumberOfFields() == 0) {
2043
                        throw new BeanException("BeanConfig has zero fields: " + bc);
2044
                }
2045
 
2046
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
2047
 
2048
                sb.append("DELETE ");
2049
 
2050
                List<Value> values = new LinkedList<Value>();
2051
 
2052
                prepareListQuery(sb, bc, bean, null, null, values);
2053
 
2054
                if (conn == null) {
2055
                        throw new BeanException("Connection is null!");
2056
                }
2 soliveira 2057
 
192 erico 2058
                PreparedStatement stmt = null;
2059
 
2060
                try {
2061
 
2062
                        if (DEBUG) {
2063
                                System.out.println("DELETE SQL: " + sb.toString());
2064
                        }
2065
 
2066
                        stmt = conn.prepareStatement(sb.toString());
2067
 
2068
                        final Iterator<Value> iter2 = values.iterator();
2069
 
2070
                        int index = 0;
2071
 
2072
                        while (iter2.hasNext()) {
2073
 
2074
                                final Value v = iter2.next();
2075
 
2076
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
2077
 
2078
                        }
2079
 
2080
                        dispatchBeforeDelete(bean);
2081
 
2082
                        final int x = stmt.executeUpdate();
2083
 
2084
                        if (DEBUG_NATIVE) {
2085
                                System.out.println("DELETE SQL (NATIVE): " + stmt);
2086
                        }
2087
 
2088
                        dispatchAfterDelete(bean);
2089
 
2090
                        return x;
2091
 
2092
                } catch (Exception e) {
2093
 
2094
                        throw new BeanException(e);
2095
 
2096
                } finally {
2097
 
2098
                        close(stmt);
2099
                }
2100
        }
2101
 
2 soliveira 2102
        @Override
4 soliveira 2103
        public boolean delete(final Object bean) {
2 soliveira 2104
 
149 erico 2105
                final BeanConfig bc = getConfigFor(bean.getClass());
2 soliveira 2106
 
2107
                if (bc.getNumberOfFields() == 0) {
2108
                        throw new BeanException("BeanConfig has zero fields: " + bc);
2109
                }
2110
 
2111
                final StringBuilder sb = new StringBuilder(32 * bc.getNumberOfFields());
2112
 
2113
                sb.append("DELETE FROM ").append(bc.getTableName()).append(" WHERE ");
2114
 
2115
                if (!bc.hasPK()) {
2116
                        throw new BeanException("Cannot delete bean without a PK!");
2117
                }
2118
 
2119
                final Iterator<DBField> iter = bc.pks();
2120
 
2121
                final List<Value> values = new LinkedList<Value>();
2122
 
2123
                int count = 0;
2124
 
2125
                while (iter.hasNext()) {
2126
 
2127
                        final DBField dbField = iter.next();
2128
 
2129
                        final String fieldName = dbField.getName();
2130
 
2131
                        final String dbFieldName = dbField.getDbName();
2132
 
2133
                        final Object value = getValueFromBean(bean, fieldName);
2134
 
2135
                        if (value == null) {
2136
                                throw new BeanException("pk is missing: " + dbField);
2137
                        } else if (value instanceof Number) {
2138
 
2139
                                final Number n = (Number) value;
2140
 
2141
                                if (n.doubleValue() <= 0) {
2142
                                        throw new BeanException("Number pk is missing: " + dbField);
2143
                                }
2144
 
2145
                        }
2146
 
2147
                        if (count++ > 0) {
2148
                                sb.append(" AND ");
2149
                        }
2150
 
2151
                        sb.append(dbFieldName).append("=?");
2152
 
2153
                        values.add(new Value(dbField, value));
2154
 
2155
                }
2156
 
2157
                if (values.isEmpty()) {
2158
                        throw new BeanException("Bean is empty: " + bean + " / " + bc);
2159
                }
2160
 
2161
                if (conn == null) {
2162
                        throw new BeanException("Connection is null!");
2163
                }
2164
 
2165
                PreparedStatement stmt = null;
2166
 
2167
                try {
2168
 
2169
                        if (DEBUG) {
2170
                                System.out.println("DELETE SQL: " + sb.toString());
2171
                        }
2172
 
2173
                        stmt = conn.prepareStatement(sb.toString());
2174
 
2175
                        final Iterator<Value> iter2 = values.iterator();
2176
 
2177
                        int index = 0;
2178
 
2179
                        while (iter2.hasNext()) {
2180
 
2181
                                final Value v = iter2.next();
2182
 
2183
                                v.field.getType().bindToStmt(stmt, ++index, v.value);
2184
 
2185
                        }
2186
 
149 erico 2187
                        dispatchBeforeDelete(bean);
2188
 
2 soliveira 2189
                        final int x = stmt.executeUpdate();
149 erico 2190
 
2191
                        if (DEBUG_NATIVE) {
2192
                                System.out.println("DELETE SQL (NATIVE): " + stmt);
2193
                        }
2 soliveira 2194
 
2195
                        if (x > 1) {
2196
                                throw new BeanException("delete modified more than one line: " + x);
2197
                        }
2198
 
2199
                        if (x == 0) {
2200
                                return false;
2201
                        }
2202
 
2203
                        loaded.remove(bean);
2204
 
149 erico 2205
                        dispatchAfterDelete(bean);
2206
 
2 soliveira 2207
                        return true;
2208
 
4 soliveira 2209
                } catch (Exception e) {
2210
 
2211
                        throw new BeanException(e);
2212
 
2 soliveira 2213
                } finally {
2214
 
11 soliveira 2215
                        close(stmt);
2 soliveira 2216
                }
2217
        }
2218
 
2219
        @Override
4 soliveira 2220
        public <E> List<E> loadList(final E bean) {
2 soliveira 2221
 
126 soliveira 2222
                return loadListImpl(bean, null, null, null, null);
2 soliveira 2223
        }
47 soliveira 2224
 
32 soliveira 2225
        @Override
126 soliveira 2226
        public <E> List<E> loadList(final E bean, Object... properties) {
2227
 
2228
                return loadListImpl(bean, null, null, getProperties(properties), null);
32 soliveira 2229
        }
114 soliveira 2230
 
2 soliveira 2231
        @Override
114 soliveira 2232
        public <E> E loadUnique(E bean) {
2233
                return loadUniqueImpl(bean, null, null);
2234
        }
2235
 
2236
        @Override
126 soliveira 2237
        public <E> E loadUnique(E bean, Object... properties) {
2238
                return loadUniqueImpl(bean, getProperties(properties), null);
114 soliveira 2239
        }
2240
 
2241
        @Override
126 soliveira 2242
        public <E> E loadUniqueMinus(E bean, Object... minus) {
2243
                return loadUniqueImpl(bean, null, getProperties(minus));
123 soliveira 2244
        }
2 soliveira 2245
 
114 soliveira 2246
        protected <E> E loadUniqueImpl(final E bean, String[] properties, String[] minus) {
2247
 
126 soliveira 2248
                E o = checkUnique(loadListImpl(bean, null, new Limit(2), properties, minus));
64 soliveira 2249
 
2250
                if (o != null) {
208 erico 2251
                        loadImpl(o, properties, minus); // load twice to attach to session so dynamic update is by default!
64 soliveira 2252
                }
2253
 
2254
                return o;
2 soliveira 2255
        }
2256
 
2257
        @Override
125 soliveira 2258
        public <E> List<E> loadList(final E bean, final OrderBy orderBy) {
2 soliveira 2259
 
126 soliveira 2260
                return loadListImpl(bean, orderBy, null, null, null);
2 soliveira 2261
        }
47 soliveira 2262
 
32 soliveira 2263
        @Override
126 soliveira 2264
        public <E> List<E> loadList(final E bean, final OrderBy orderBy, Object... properties) {
2265
                return loadListImpl(bean, orderBy, null, getProperties(properties), null);
32 soliveira 2266
        }
2 soliveira 2267
 
2268
        @Override
126 soliveira 2269
        public <E> List<E> loadList(final E bean, final Limit limit) {
2 soliveira 2270
 
2271
                return loadList(bean, null, limit);
2272
        }
47 soliveira 2273
 
32 soliveira 2274
        @Override
126 soliveira 2275
        public <E> List<E> loadList(final E bean, final Limit limit, Object... properties) {
2276
                return loadListImpl(bean, null, limit, getProperties(properties), null);
32 soliveira 2277
        }
2 soliveira 2278
 
12 soliveira 2279
        /**
22 soliveira 2280
         * 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 2281
         *
2282
         * @param <E>
2283
         * @param bean
2284
         * @param minus
13 soliveira 2285
         * @return A list of beans
12 soliveira 2286
         */
32 soliveira 2287
        @Override
126 soliveira 2288
        public <E> List<E> loadListMinus(final E bean, final Object... minus) {
2289
 
2290
                return loadListMinus(bean, null, null, minus);
2 soliveira 2291
        }
2292
 
12 soliveira 2293
        /**
22 soliveira 2294