OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
17 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.sql.model;
15
 
16
import org.openconcerto.utils.Tuple2;
17
 
18
import java.sql.ResultSet;
19
import java.sql.SQLException;
20
import java.util.ArrayList;
174 ilm 21
import java.util.Collection;
17 ilm 22
import java.util.List;
174 ilm 23
import java.util.stream.Collectors;
17 ilm 24
 
25
import org.apache.commons.dbutils.ResultSetHandler;
26
 
27
public final class SQLRowListRSH implements ResultSetHandler {
28
 
63 ilm 29
    // hashCode()/equals() needed for data source cache
83 ilm 30
    public static final class RSH implements ResultSetHandler {
63 ilm 31
        private final Tuple2<SQLTable, List<String>> names;
182 ilm 32
        private final boolean immutableRows;
63 ilm 33
 
83 ilm 34
        // allow to create rows from arbitrary columns (and not just directly from actual fields of
35
        // the same table)
36
        // ATTN doesn't check that the types of columns are coherent with the types of the fields
37
        public RSH(final SQLTable t, final List<String> names) {
182 ilm 38
            this(Tuple2.create(t, names), true);
174 ilm 39
            // null are OK (they're ignored)
40
            final List<String> unknown = names.stream().filter(n -> n != null && !t.getFieldsName().contains(n)).collect(Collectors.toList());
41
            if (!unknown.isEmpty())
42
                throw new IllegalArgumentException("Not all names are fields of " + t + " : " + unknown);
83 ilm 43
        }
44
 
182 ilm 45
        private RSH(final Tuple2<SQLTable, List<String>> names, final boolean immutableRows) {
63 ilm 46
            this.names = names;
182 ilm 47
            this.immutableRows = immutableRows;
63 ilm 48
        }
49
 
50
        @Override
174 ilm 51
        public List<SQLRow> handle(final ResultSet rs) throws SQLException {
83 ilm 52
            // since the result will be cached, disallow its modification (e.g.avoid
53
            // ConcurrentModificationException)
182 ilm 54
            return SQLRow.createListFromRS(this.names.get0(), rs, this.names.get1(), this.immutableRows);
63 ilm 55
        }
56
 
57
        @Override
58
        public int hashCode() {
59
            return this.names.hashCode();
60
        }
61
 
62
        @Override
174 ilm 63
        public boolean equals(final Object obj) {
63 ilm 64
            if (this == obj)
65
                return true;
66
            if (obj == null)
67
                return false;
68
            if (getClass() != obj.getClass())
69
                return false;
70
            final RSH other = (RSH) obj;
71
            return this.names.equals(other.names);
72
        }
73
    }
74
 
83 ilm 75
    private static TableRef checkTable(final TableRef t) {
76
        if (t == null)
77
            throw new IllegalArgumentException("null table");
78
        if (!t.getTable().isRowable())
79
            throw new IllegalArgumentException("table isn't rowable : " + t);
80
        return t;
81
    }
82
 
174 ilm 83
    static Tuple2<SQLTable, List<String>> getIndexes(final SQLSelect sel, final TableRef passedTable, final boolean findTable) {
83 ilm 84
        final List<FieldRef> selectFields = sel.getSelectFields();
17 ilm 85
        final int size = selectFields.size();
86
        if (size == 0)
87
            throw new IllegalArgumentException("empty select : " + sel);
83 ilm 88
        TableRef t;
17 ilm 89
        if (findTable) {
90
            if (passedTable != null)
91
                throw new IllegalArgumentException("non null table " + passedTable);
83 ilm 92
            t = null;
17 ilm 93
        } else {
83 ilm 94
            t = checkTable(passedTable);
17 ilm 95
        }
96
        final List<String> l = new ArrayList<String>(size);
97
        for (int i = 0; i < size; i++) {
83 ilm 98
            final FieldRef field = selectFields.get(i);
99
            if (field == null) {
100
                // computed field
17 ilm 101
                l.add(null);
83 ilm 102
            } else {
103
                if (t == null) {
104
                    assert findTable;
105
                    t = checkTable(field.getTableRef());
106
                }
107
                assert t != null && t.getTable().isRowable();
108
 
109
                if (field.getTableRef().equals(t)) {
110
                    l.add(field.getField().getName());
111
                } else if (findTable) {
112
                    // prevent ambiguity : either specify a table or there must be only one table
113
                    throw new IllegalArgumentException(field + " is not in " + t);
114
                } else {
115
                    l.add(null);
116
                }
117
            }
17 ilm 118
        }
83 ilm 119
        return Tuple2.create(t.getTable(), l);
17 ilm 120
    }
121
 
122
    /**
123
     * Create a handler that don't need metadata.
174 ilm 124
     *
17 ilm 125
     * @param sel the select that will produce the result set, must only have one table.
126
     * @return a handler creating a list of {@link SQLRow}.
93 ilm 127
     * @deprecated use {@link SQLSelectHandlerBuilder}
17 ilm 128
     */
174 ilm 129
    @Deprecated
17 ilm 130
    static public ResultSetHandler createFromSelect(final SQLSelect sel) {
182 ilm 131
        return create(getIndexes(sel, null, true), false);
17 ilm 132
    }
133
 
134
    /**
135
     * Create a handler that don't need metadata. Useful since some JDBC drivers perform queries for
136
     * each metadata.
174 ilm 137
     *
17 ilm 138
     * @param sel the select that will produce the result set.
83 ilm 139
     * @param t the table for which to create rows.
17 ilm 140
     * @return a handler creating a list of {@link SQLRow}.
93 ilm 141
     * @deprecated use {@link SQLSelectHandlerBuilder}
17 ilm 142
     */
174 ilm 143
    @Deprecated
83 ilm 144
    static public ResultSetHandler createFromSelect(final SQLSelect sel, final TableRef t) {
182 ilm 145
        return create(getIndexes(sel, t, false), false);
17 ilm 146
    }
147
 
182 ilm 148
    static ResultSetHandler create(final Tuple2<SQLTable, List<String>> names, final boolean immutableRows) {
149
        return new RSH(names, immutableRows);
17 ilm 150
    }
151
 
182 ilm 152
    static public List<SQLRow> fetch(final SQLTable t) throws IllegalArgumentException {
153
        return fetch(t, null);
154
    }
155
 
174 ilm 156
    static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids) throws IllegalArgumentException {
157
        return fetch(t, ids, null);
158
    }
159
 
160
    static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids, final Collection<String> fields) throws IllegalArgumentException {
161
        final SQLSelect sel = new SQLSelect();
162
        if (fields == null)
163
            sel.addSelectStar(t);
164
        else
165
            sel.addAllSelect(t, fields);
182 ilm 166
        // deterministic
167
        sel.addOrder(t, false);
168
        if (ids != null)
169
            sel.setWhere(new Where(t.getKey(), ids));
174 ilm 170
        return execute(sel);
171
    }
172
 
93 ilm 173
    /**
174
     * Execute the passed select and return rows. NOTE if there's more than one table in the query
175
     * {@link #execute(SQLSelect, TableRef)} must be used.
174 ilm 176
     *
93 ilm 177
     * @param sel the query to execute.
178
     * @return rows.
179
     * @throws IllegalArgumentException if there's more than one table in the query.
180
     */
181
    static public List<SQLRow> execute(final SQLSelect sel) throws IllegalArgumentException {
182
        return execute(sel, true, true);
17 ilm 183
    }
184
 
93 ilm 185
    static public List<SQLRow> execute(final SQLSelect sel, final boolean readCache, final boolean writeCache) {
186
        return new SQLSelectHandlerBuilder(sel).setReadCache(readCache).setWriteCache(writeCache).execute();
187
    }
188
 
189
    /**
190
     * Execute the passed select and return rows of <code>t</code>. NOTE if there's only one table
191
     * in the query {@link #execute(SQLSelect)} should be used.
174 ilm 192
     *
93 ilm 193
     * @param sel the query to execute.
194
     * @param t the table to use.
195
     * @return rows of <code>t</code>.
196
     * @throws NullPointerException if <code>t</code> is <code>null</code>.
197
     */
198
    static public List<SQLRow> execute(final SQLSelect sel, final TableRef t) throws NullPointerException {
199
        return new SQLSelectHandlerBuilder(sel).setTableRef(t).execute();
200
    }
201
 
17 ilm 202
    private final SQLTable t;
203
    private final boolean tableOnly;
204
 
174 ilm 205
    public SQLRowListRSH(final SQLTable t) {
17 ilm 206
        this(t, false);
207
    }
208
 
174 ilm 209
    public SQLRowListRSH(final SQLTable t, final boolean tableOnly) {
17 ilm 210
        super();
211
        this.t = t;
212
        this.tableOnly = tableOnly;
213
    }
214
 
174 ilm 215
    @Override
216
    public List<SQLRow> handle(final ResultSet rs) throws SQLException {
17 ilm 217
        return SQLRow.createListFromRS(this.t, rs, this.tableOnly);
218
    }
219
}