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 |
}
|