OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 156 Rev 182
Line 1... Line 1...
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
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
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
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.
9
 * language governing permissions and limitations under the License.
Line 21... Line 21...
21
import org.openconcerto.sql.model.SQLSelect.LockStrength;
21
import org.openconcerto.sql.model.SQLSelect.LockStrength;
22
import org.openconcerto.sql.model.SQLTable.VirtualFields;
22
import org.openconcerto.sql.model.SQLTable.VirtualFields;
23
import org.openconcerto.sql.model.graph.Link;
23
import org.openconcerto.sql.model.graph.Link;
24
import org.openconcerto.sql.model.graph.Link.Direction;
24
import org.openconcerto.sql.model.graph.Link.Direction;
25
import org.openconcerto.sql.model.graph.Path;
25
import org.openconcerto.sql.model.graph.Path;
-
 
26
import org.openconcerto.utils.CollectionUtils;
26
import org.openconcerto.utils.ListMap;
27
import org.openconcerto.utils.ListMap;
27
import org.openconcerto.utils.SetMap;
28
import org.openconcerto.utils.SetMap;
28
import org.openconcerto.utils.Tuple2.List2;
29
import org.openconcerto.utils.Tuple2.List2;
29
 
30
 
30
import java.sql.ResultSet;
31
import java.sql.ResultSet;
Line 40... Line 41...
40
import java.util.LinkedHashSet;
41
import java.util.LinkedHashSet;
41
import java.util.List;
42
import java.util.List;
42
import java.util.Map;
43
import java.util.Map;
43
import java.util.Map.Entry;
44
import java.util.Map.Entry;
44
import java.util.Set;
45
import java.util.Set;
-
 
46
import java.util.function.BiFunction;
45
import java.util.logging.Level;
47
import java.util.logging.Level;
46
 
48
 
47
import org.apache.commons.dbutils.ResultSetHandler;
49
import org.apache.commons.dbutils.ResultSetHandler;
48
 
50
 
49
/**
51
/**
Line 100... Line 102...
100
     * @return la ligne correspondante.
102
     * @return la ligne correspondante.
101
     * @throws SQLException si problème lors de l'accès au ResultSet.
103
     * @throws SQLException si problème lors de l'accès au ResultSet.
102
     * @see SQLRow#SQLRow(SQLTable, Map)
104
     * @see SQLRow#SQLRow(SQLTable, Map)
103
     * @deprecated use {@link SQLRowListRSH} or {@link SQLRowValuesListFetcher} instead or if you
105
     * @deprecated use {@link SQLRowListRSH} or {@link SQLRowValuesListFetcher} instead or if you
104
     *             must use a {@link ResultSet} call
106
     *             must use a {@link ResultSet} call
105
     *             {@link #createFromRS(SQLTable, ResultSet, ResultSetMetaData, boolean)} thus
107
     *             {@link #createListFromRS(SQLTable, ResultSet, boolean)} thus avoiding the
106
     *             avoiding the potentially costly {@link ResultSet#getMetaData()}
108
     *             potentially costly {@link ResultSet#getMetaData()}
107
     */
109
     */
108
    public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final boolean onlyTable) throws SQLException {
110
    public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final boolean onlyTable) throws SQLException {
109
        return createFromRS(table, rs, rs.getMetaData(), onlyTable);
111
        return createFromRS(table, rs, rs.getMetaData(), onlyTable);
110
    }
112
    }
111
 
113
 
-
 
114
    // see createListFromRS()
-
 
115
    @Deprecated
112
    public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final ResultSetMetaData rsmd, final boolean onlyTable) throws SQLException {
116
    public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final ResultSetMetaData rsmd, final boolean onlyTable) throws SQLException {
113
        return createFromRS(table, rs, getFieldNames(table, rsmd, onlyTable));
117
        return createFromRS(table, rs, new SQLFieldRowProcessor(table, getFieldNames(table, rsmd, onlyTable)), true);
114
    }
118
    }
115
 
119
 
116
    private static final List<String> getFieldNames(SQLTable table, final ResultSetMetaData rsmd, final boolean tableOnly) throws SQLException {
120
    private static final List<String> getFieldNames(SQLTable table, final ResultSetMetaData rsmd, final boolean tableOnly) throws SQLException {
117
        final int colCount = rsmd.getColumnCount();
121
        final int colCount = rsmd.getColumnCount();
118
        final List<String> names = new ArrayList<String>(colCount);
122
        final List<String> names = new ArrayList<String>(colCount);
Line 128... Line 132...
128
        }
132
        }
129
 
133
 
130
        return names;
134
        return names;
131
    }
135
    }
132
 
136
 
133
    // MAYBE create an opaque class holding names so that we can make this method, getFieldNames()
-
 
134
    // and createListFromRS() public
137
    // ATTN doesn't check that names are fields of table
135
    static final SQLRow createFromRS(SQLTable table, ResultSet rs, final List<String> names) throws SQLException {
138
    public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final SQLFieldRowProcessor rowProc, final boolean immutable) throws SQLException {
136
        final int indexCount = names.size();
-
 
137
 
-
 
138
        final Map<String, Object> m = new HashMap<String, Object>(indexCount);
139
        final Map<String, Object> m = rowProc.toMap(rs);
139
        for (int i = 0; i < indexCount; i++) {
-
 
140
            final String colName = names.get(i);
-
 
141
            if (colName != null)
-
 
142
                m.put(colName, rs.getObject(i + 1));
-
 
143
        }
-
 
144
 
140
 
145
        final Number id = getID(m, table, true);
141
        final Number id = getID(m, table, true);
146
        // e.g. LEFT JOIN : missing values are null
142
        // e.g. LEFT JOIN : missing values are null
147
        if (id == null)
143
        if (id == null)
148
            return null;
144
            return null;
149
 
145
 
150
        // pass already found ID
146
        // pass already found ID
-
 
147
        final SQLRow res = new SQLRow(table, id, Collections.unmodifiableMap(m));
-
 
148
        if (immutable)
-
 
149
            res.freeze();
151
        return new SQLRow(table, id, m);
150
        return res;
152
    }
151
    }
153
 
152
 
154
    /**
153
    /**
155
     * Create a list of rows using the metadata to find the columns' names.
154
     * Create a list of rows using the metadata to find the columns' names.
156
     * 
155
     * 
Line 160... Line 159...
160
     *        <code>table</code>.
159
     *        <code>table</code>.
161
     * @return the data of the result set as SQLRows.
160
     * @return the data of the result set as SQLRows.
162
     * @throws SQLException if an error occurs while reading <code>rs</code>.
161
     * @throws SQLException if an error occurs while reading <code>rs</code>.
163
     */
162
     */
164
    public static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final boolean tableOnly) throws SQLException {
163
    public static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final boolean tableOnly) throws SQLException {
165
        return createListFromRS(table, rs, getFieldNames(table, rs.getMetaData(), tableOnly));
164
        return createListFromRS(table, rs, getFieldNames(table, rs.getMetaData(), tableOnly), true);
166
    }
165
    }
167
 
166
 
168
    /**
167
    /**
169
     * Create a list of rows without using the metadata.
168
     * Create a list of rows without using the metadata.
170
     * 
169
     * 
171
     * @param table the table of the rows.
170
     * @param table the table of the rows.
172
     * @param rs the result set.
171
     * @param rs the result set.
173
     * @param names the name of the field for each column, nulls are ignored, e.g. ["DESIGNATION",
172
     * @param names the name of the field for each column, nulls are ignored, e.g. ["DESIGNATION",
174
     *        null, "ID"].
173
     *        null, "ID"].
-
 
174
     * @param immutable <code>true</code> if the result should be immutable.
175
     * @return the data of the result set as SQLRows.
175
     * @return the data of the result set as SQLRows.
176
     * @throws SQLException if an error occurs while reading <code>rs</code>.
176
     * @throws SQLException if an error occurs while reading <code>rs</code>.
177
     */
177
     */
178
    static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final List<String> names) throws SQLException {
178
    static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final List<String> names, final boolean immutable) throws SQLException {
179
        final List<SQLRow> res = new ArrayList<SQLRow>();
179
        final List<SQLRow> res = new ArrayList<SQLRow>();
-
 
180
        final SQLFieldRowProcessor rowProc = new SQLFieldRowProcessor(table, names);
180
        while (rs.next()) {
181
        while (rs.next()) {
181
            final SQLRow row = createFromRS(table, rs, names);
182
            final SQLRow row = createFromRS(table, rs, rowProc, immutable);
182
            if (row != null)
183
            if (row != null)
183
                res.add(row);
184
                res.add(row);
184
        }
185
        }
185
        return res;
186
        return immutable ? Collections.unmodifiableList(res) : res;
186
    }
187
    }
187
 
188
 
188
    static final SQLRow createFromSelect(final SQLTable t, final VirtualFields vfs, final int id, final LockStrength l) {
189
    static final SQLRow createFromSelect(final SQLTable t, final VirtualFields vfs, final int id, final LockStrength l) {
189
        final SQLSelect sel = new SQLSelect(true).addAllSelect(t.getFields(vfs));
190
        final SQLSelect sel = new SQLSelect(true).addAllSelect(t.getFields(vfs));
190
        sel.setLockStrength(l);
191
        sel.setLockStrength(l);
191
        sel.setWhere(new Where(t.getKey(), "=", id));
192
        sel.setWhere(new Where(t.getKey(), "=", id));
192
        @SuppressWarnings("unchecked")
193
        @SuppressWarnings("unchecked")
193
        final Map<String, ?> map = (Map<String, ?>) t.getDBSystemRoot().getDataSource().execute(sel.asString(), new IResultSetHandler(SQLDataSource.MAP_HANDLER, l.equals(LockStrength.NONE)));
194
        final Map<String, Object> map = (Map<String, Object>) t.getDBSystemRoot().getDataSource().execute(sel.asString(),
-
 
195
                new IResultSetHandler(SQLDataSource.MAP_HANDLER, l.equals(LockStrength.NONE)));
194
        return new SQLRow(t, id, map);
196
        return new SQLRow(t, id, map == null ? null : Collections.unmodifiableMap(map));
195
    }
197
    }
196
 
198
 
197
    /**
199
    /**
198
     * Create an empty existing row (without checking the DB).
200
     * Create an empty existing row (without checking the DB).
199
     * 
201
     * 
200
     * @param t the table.
202
     * @param t the table.
201
     * @param id the ID.
203
     * @param id the ID.
202
     * @return a new {@link #exists() existing} {@link #isFilled() filled} {@link #getFields()
204
     * @return a new {@link #exists() existing} {@link #isFilled() filled} {@link #isFrozen()
203
     *         empty} row.
205
     *         frozen} {@link #getFields() empty} row.
204
     */
206
     */
205
    static final SQLRow createEmpty(final SQLTable t, final int id) {
207
    public static final SQLRow createEmpty(final SQLTable t, final int id) {
206
        return new SQLRow(t, id, Collections.<String, Object> emptyMap());
208
        final SQLRow res = new SQLRow(t, id, Collections.<String, Object> emptyMap());
-
 
209
        res.freeze();
-
 
210
        return res;
-
 
211
    }
-
 
212
 
-
 
213
    public static final <R extends SQLRowAccessor, A> SQLRow trim(final R r, final BiFunction<? super R, ? super A, ? extends Map<String, Object>> getVals, final A arg) {
-
 
214
        final SQLRow res = new SQLRow(r.getTable(), null, CollectionUtils.toImmutableMap(getVals.apply(r, arg)));
-
 
215
        res.freeze();
-
 
216
        return res;
207
    }
217
    }
208
 
218
 
209
    private final int ID;
219
    private final int ID;
210
    private final Number idNumber;
220
    private final Number idNumber;
211
    private Map<String, Object> values;
221
    private Map<String, Object> values;
212
    private boolean fetched;
222
    private boolean fetched;
-
 
223
    private boolean frozen = false;
213
 
224
 
214
    private SQLRow(SQLTable table, Number id) {
225
    private SQLRow(SQLTable table, Number id) {
215
        super(table);
226
        super(table);
216
        this.fetched = false;
227
        this.fetched = false;
217
        this.ID = id.intValue();
228
        this.ID = id.intValue();
Line 237... Line 248...
237
     * @param table la table.
248
     * @param table la table.
238
     * @param values les valeurs de la lignes.
249
     * @param values les valeurs de la lignes.
239
     * @throws IllegalArgumentException si values ne contient pas la clef de la table.
250
     * @throws IllegalArgumentException si values ne contient pas la clef de la table.
240
     */
251
     */
241
    public SQLRow(SQLTable table, Map<String, ?> values) {
252
    public SQLRow(SQLTable table, Map<String, ?> values) {
242
        this(table, null, values);
253
        this(table, null, values == null ? null : CollectionUtils.toImmutableMap(values));
243
    }
254
    }
244
 
255
 
245
    // allow to call getID() only once
256
    // allow to call getID() only once
-
 
257
    // Private since it doesn't make sure the map is immutable.
246
    private SQLRow(SQLTable table, final Number id, Map<String, ?> values) {
258
    private SQLRow(SQLTable table, final Number id, Map<String, Object> values) {
247
        this(table, id == null ? getID(values, table, false) : id);
259
        this(table, id == null ? getID(values, table, false) : id);
248
        // faire une copie, sinon backdoor pour changer les valeurs sans qu'on s'en aperçoive
-
 
249
        this.setValues(values == null ? null : new HashMap<String, Object>(values));
260
        this.setValues(values);
250
    }
261
    }
251
 
262
 
252
    // return ID, must always be present but may be null if <code>nullAllowed</code>
263
    // return ID, must always be present but may be null if <code>nullAllowed</code>
253
    private static Number getID(Map<String, ?> values, final SQLTable table, final boolean nullAllowed) {
264
    private static Number getID(Map<String, ?> values, final SQLTable table, final boolean nullAllowed) {
254
        final String keyName = table.getKey().getName();
265
        final String keyName = table.getKey().getName();
Line 263... Line 274...
263
            final String valS = keyValue == null ? "' is null" : "' isn't a Number : " + keyValue.getClass() + " " + keyValue;
274
            final String valS = keyValue == null ? "' is null" : "' isn't a Number : " + keyValue.getClass() + " " + keyValue;
264
            throw new IllegalArgumentException("The value of '" + keyName + valS);
275
            throw new IllegalArgumentException("The value of '" + keyName + valS);
265
        }
276
        }
266
    }
277
    }
267
 
278
 
-
 
279
    public final SQLRow copy(final boolean freeze) {
-
 
280
        final SQLRow res = new SQLRow(this.getTable(), this.getIDNumber());
-
 
281
        if (this.isFilled())
-
 
282
            // safe to share map, since it is immutable
-
 
283
            res.setValues(this.getValues());
-
 
284
        assert res.isFilled() == this.isFilled();
-
 
285
        if (freeze)
-
 
286
            res.freeze();
-
 
287
        assert res.isFrozen() == freeze;
-
 
288
        return res;
-
 
289
    }
-
 
290
 
-
 
291
    @Override
-
 
292
    public final boolean isFrozen() {
-
 
293
        return this.frozen;
-
 
294
    }
-
 
295
 
-
 
296
    private void checkFrozen() {
-
 
297
        if (this.isFrozen())
-
 
298
            throw new IllegalStateException("SQLRow is not modifiable");
-
 
299
    }
-
 
300
 
-
 
301
    /**
-
 
302
     * Freeze this instance so that no modification can be made. Once this method returns, this
-
 
303
     * instance can be safely published (e.g. stored into a field that is properly guarded by a
-
 
304
     * lock) to other threads without further synchronizations.
-
 
305
     * 
-
 
306
     * @return <code>true</code> if this call changed the frozen status.
-
 
307
     */
-
 
308
    public final boolean freeze() {
-
 
309
        if (this.frozen)
-
 
310
            return false;
-
 
311
        this.frozen = true;
-
 
312
        return true;
-
 
313
    }
-
 
314
 
-
 
315
    @Override
-
 
316
    public final SQLRow toImmutable() {
-
 
317
        if (this.isFrozen())
-
 
318
            return this;
-
 
319
        return this.copy(true);
-
 
320
    }
-
 
321
 
268
    /**
322
    /**
269
     * Whether this contains values or just the {@link #getIDNumber() id}. NOTE that
323
     * Whether this contains values or just the {@link #getIDNumber() id}. NOTE that
270
     * {@link #getObject(String)} (and thus any other methods that call it) will access the DB if
324
     * {@link #getObject(String)} (and thus any other methods that call it) will access the DB if
271
     * the requested field is {@link #getFields() missing} even if this returns <code>true</code>.
325
     * the requested field is {@link #getFields() missing} even if this returns <code>true</code>.
272
     * 
326
     * 
Line 311... Line 365...
311
     * 
365
     * 
312
     * @param useCache <code>true</code> to use the {@link SQLDataSource#isCacheEnabled() cache}.
366
     * @param useCache <code>true</code> to use the {@link SQLDataSource#isCacheEnabled() cache}.
313
     * @return a new instance.
367
     * @return a new instance.
314
     */
368
     */
315
    public final SQLRow fetchNew(final boolean useCache) {
369
    public final SQLRow fetchNew(final boolean useCache) {
316
        return new SQLRow(this.getTable(), this.getIDNumber()).fetchValues(useCache);
370
        return this.fetchNewRow(useCache);
317
    }
371
    }
318
 
372
 
319
    @SuppressWarnings("unchecked")
373
    @SuppressWarnings("unchecked")
320
    SQLRow fetchValues(final boolean readCache, final boolean writeCache) {
374
    SQLRow fetchValues(final boolean readCache, final boolean writeCache) {
-
 
375
        // not necessary here, but allows to fail early and avoid a request
-
 
376
        // implique trop de regression dans la branche : checkFrozen();
321
        final IResultSetHandler handler = new IResultSetHandler(SQLDataSource.MAP_HANDLER, readCache, writeCache) {
377
        final IResultSetHandler handler = new IResultSetHandler(SQLDataSource.MAP_HANDLER, readCache, writeCache) {
322
            @Override
378
            @Override
323
            public Set<SQLRow> getCacheModifiers() {
379
            public Set<SQLRow> getCacheModifiers() {
324
                return Collections.singleton(SQLRow.this);
380
                return Collections.singleton(SQLRow.this);
325
            }
381
            }
326
        };
382
        };
327
        this.setValues((Map<String, Object>) this.getTable().getBase().getDataSource().execute(this.getQuery(), handler, false));
383
        final Map<String, Object> values = (Map<String, Object>) this.getTable().getBase().getDataSource().execute(this.getQuery(), handler, false);
-
 
384
        this.setValues(values == null ? null : Collections.unmodifiableMap(values));
328
        return this;
385
        return this;
329
    }
386
    }
330
 
387
 
331
    // attention ne vérifie pas que tous les champs soient présents
388
    // Attention ne vérifie pas que tous les champs soient présents.
-
 
389
    // Private since it doesn't make sure the map is immutable.
332
    private final void setValues(Map<String, Object> values) {
390
    private final void setValues(Map<String, Object> values) {
-
 
391
        // implique trop de regression dans la branche : checkFrozen();
333
        this.values = values;
392
        this.values = values;
334
        if (!this.fetched)
393
        if (!this.fetched)
335
            this.fetched = true;
394
            this.fetched = true;
336
    }
395
    }
337
 
396
 
Line 340... Line 399...
340
     * 
399
     * 
341
     * @return les noms des champs qui ont été chargé depuis la base.
400
     * @return les noms des champs qui ont été chargé depuis la base.
342
     */
401
     */
343
    @Override
402
    @Override
344
    public Set<String> getFields() {
403
    public Set<String> getFields() {
345
        return this.fetched ? Collections.unmodifiableSet(this.getValues().keySet()) : Collections.<String> emptySet();
404
        return this.isFilled() ? this.getValues().keySet() : Collections.<String> emptySet();
346
    }
405
    }
347
 
406
 
348
    // avoid Collections.unmodifiableSet() allocation
407
    // avoid Collections.unmodifiableSet() allocation
349
    @Override
408
    @Override
350
    public boolean contains(String fieldName) {
409
    public boolean contains(String fieldName) {
351
        return this.fetched ? this.getValues().containsKey(fieldName) : false;
410
        return this.isFilled() ? this.getValues().containsKey(fieldName) : false;
352
    }
411
    }
353
 
412
 
354
    private String getQuery() {
413
    private String getQuery() {
355
        return "SELECT * FROM " + this.getTable().getSQLName().quote() + " WHERE " + this.getWhere().getClause();
414
        return "SELECT * FROM " + this.getTable().getSQLName().quote() + " WHERE " + this.getWhere().getClause();
356
    }
415
    }
Line 409... Line 468...
409
        }
468
        }
410
        assert this.getValues().containsKey(field);
469
        assert this.getValues().containsKey(field);
411
        return this.getValues().get(field);
470
        return this.getValues().get(field);
412
    }
471
    }
413
 
472
 
-
 
473
    @Override
-
 
474
    public final Object getObjectNoCheck(String field) {
-
 
475
        return this.values.get(field);
-
 
476
    }
-
 
477
 
414
    /**
478
    /**
415
     * Fetch from the DB this row and the next/previous one. ATTN the rows are locked
479
     * Fetch from the DB this row and the next/previous one. ATTN the rows are locked
416
     * {@link LockStrength#UPDATE for update}, but if this method is not called from within a
480
     * {@link LockStrength#UPDATE for update}, but if this method is not called from within a
417
     * transaction, they will immediately be obsolete.
481
     * transaction, they will immediately be obsolete.
418
     * 
482
     * 
Line 1038... Line 1102...
1038
     * @see #getAllValues()
1102
     * @see #getAllValues()
1039
     * @see #getObject(String)
1103
     * @see #getObject(String)
1040
     */
1104
     */
1041
    @Override
1105
    @Override
1042
    public Map<String, Object> getAbsolutelyAll() {
1106
    public Map<String, Object> getAbsolutelyAll() {
1043
        return Collections.unmodifiableMap(this.getValues());
1107
        return this.getValues();
1044
    }
1108
    }
1045
 
1109
 
1046
    private static final VirtualFields ALL_VALUES_FIELDS = VirtualFields.ALL.difference(VirtualFields.KEYS, VirtualFields.ARCHIVE, VirtualFields.ORDER);
1110
    private static final VirtualFields ALL_VALUES_FIELDS = VirtualFields.ALL.difference(VirtualFields.KEYS, VirtualFields.ARCHIVE, VirtualFields.ORDER);
1047
 
1111
 
1048
    /**
1112
    /**
Line 1091... Line 1155...
1091
    public Number getIDNumber() {
1155
    public Number getIDNumber() {
1092
        return this.idNumber;
1156
        return this.idNumber;
1093
    }
1157
    }
1094
 
1158
 
1095
    @Override
1159
    @Override
1096
    public SQLRow asRow() {
1160
    public SQLRow asRow(final Boolean immutable) {
-
 
1161
        if (immutable == null || this.isFrozen() == immutable)
1097
        return this;
1162
            return this;
-
 
1163
        else
-
 
1164
            return this.copy(immutable);
1098
    }
1165
    }
1099
 
1166
 
1100
    @Override
1167
    @Override
1101
    public final SQLRowValues asRowValues() {
1168
    public final SQLRowValues asRowValues(final Boolean immutable) {
1102
        return this.createUpdateRow();
1169
        final SQLRowValues res = this.createUpdateRow();
-
 
1170
        if (Boolean.TRUE.equals(immutable))
-
 
1171
            res.getGraph().freeze();
-
 
1172
        return res;
1103
    }
1173
    }
1104
 
1174
 
1105
    /**
1175
    /**
1106
     * Note : ne compare pas les valeurs des champs de cette ligne.
1176
     * Note : ne compare pas les valeurs des champs de cette ligne.
1107
     * 
1177
     *