Skip to contentMethod: exceptionCausesRollback(RuntimeException)
1: /**
2: * Copyright (C) 2020 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. 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: package cz.cvut.kbss.jopa.model;
16:
17: import cz.cvut.kbss.jopa.exceptions.NoResultException;
18: import cz.cvut.kbss.jopa.exceptions.NoUniqueResultException;
19: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
20: import cz.cvut.kbss.jopa.model.query.Parameter;
21: import cz.cvut.kbss.jopa.model.query.Query;
22: import cz.cvut.kbss.jopa.query.QueryHolder;
23: import cz.cvut.kbss.jopa.sessions.ConnectionWrapper;
24: import cz.cvut.kbss.jopa.utils.ErrorUtils;
25: import cz.cvut.kbss.jopa.utils.Procedure;
26: import cz.cvut.kbss.jopa.utils.ThrowingConsumer;
27: import cz.cvut.kbss.ontodriver.ResultSet;
28: import cz.cvut.kbss.ontodriver.Statement;
29: import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
30: import cz.cvut.kbss.ontodriver.iteration.ResultRow;
31: import org.slf4j.Logger;
32: import org.slf4j.LoggerFactory;
33:
34: import java.util.Objects;
35: import java.util.Optional;
36: import java.util.Set;
37: import java.util.function.Function;
38: import java.util.stream.Stream;
39: import java.util.stream.StreamSupport;
40:
41: /**
42: * Common state and behavior of both {@link cz.cvut.kbss.jopa.model.query.Query} and {@link
43: * cz.cvut.kbss.jopa.model.query.TypedQuery} implementations.
44: */
45: abstract class AbstractQuery implements Query {
46:
47: private static final Logger LOG = LoggerFactory.getLogger(AbstractQuery.class);
48:
49: final QueryHolder query;
50: private final ConnectionWrapper connection;
51:
52: private boolean useBackupOntology = false;
53:
54: private Procedure rollbackOnlyMarker;
55: private Procedure ensureOpenProcedure;
56:
57: AbstractQuery(QueryHolder query, ConnectionWrapper connection) {
58: this.query = Objects.requireNonNull(query, ErrorUtils.getNPXMessageSupplier("query"));
59: this.connection = Objects.requireNonNull(connection, ErrorUtils.getNPXMessageSupplier("connection"));
60: }
61:
62: /**
63: * Sets ontology used for processing of this query.
64: *
65: * @param useBackupOntology If true, the backup (central) ontology is used, otherwise the transactional ontology is
66: * used (default)
67: */
68: public void useBackupOntology(boolean useBackupOntology) {
69: this.useBackupOntology = useBackupOntology;
70: }
71:
72: private void logQuery() {
73: if (LOG.isTraceEnabled()) {
74: LOG.trace("Executing query: {}", query.assembleQuery());
75: }
76: }
77:
78: private void setTargetOntology(Statement stmt) {
79: if (useBackupOntology) {
80: stmt.useOntology(Statement.StatementOntology.CENTRAL);
81: } else {
82: stmt.useOntology(Statement.StatementOntology.TRANSACTIONAL);
83: }
84: }
85:
86: OWLPersistenceException queryEvaluationException(OntoDriverException e) {
87: final String executedQuery = query.assembleQuery();
88: return new OWLPersistenceException("Exception caught when evaluating query " + executedQuery, e);
89: }
90:
91: void markTransactionForRollback() {
92: if (rollbackOnlyMarker != null) {
93: rollbackOnlyMarker.execute();
94: }
95: }
96:
97: void ensureOpen() {
98: assert ensureOpenProcedure != null;
99: ensureOpenProcedure.execute();
100: }
101:
102: /**
103: * Registers reference to a method which marks current transaction (if active) for rollback on exceptions.
104: *
105: * @param rollbackOnlyMarker The marker to invoke on exceptions
106: */
107: void setRollbackOnlyMarker(Procedure rollbackOnlyMarker) {
108: this.rollbackOnlyMarker = rollbackOnlyMarker;
109: }
110:
111: /**
112: * Registers a reference to a method which ensures that the query is called on an open persistence context.
113: * <p>
114: * This method is likely to come from an {@link EntityManager} instance which was used to create this query.
115: *
116: * @param ensureOpenProcedure The procedure to call when ensuring that persistence context is open
117: */
118: void setEnsureOpenProcedure(Procedure ensureOpenProcedure) {
119: this.ensureOpenProcedure = ensureOpenProcedure;
120: }
121:
122: private static IllegalStateException unboundParam(Object param) {
123: return new IllegalStateException("Parameter " + param + " is not bound.");
124: }
125:
126: @Override
127: public void executeUpdate() {
128: ensureOpen();
129: final Statement stmt = connection.createStatement();
130: try {
131: setTargetOntology(stmt);
132: logQuery();
133: stmt.executeUpdate(query.assembleQuery());
134: } catch (OntoDriverException e) {
135: markTransactionForRollback();
136: throw queryEvaluationException(e);
137: } catch (RuntimeException e) {
138: markTransactionForRollback();
139: throw e;
140: } finally {
141: try {
142: stmt.close();
143: } catch (Exception e) {
144: LOG.error("Unable to close statement after update execution.", e);
145: }
146: }
147: }
148:
149: @Override
150: public Parameter<?> getParameter(int position) {
151: return query.getParameter(position);
152: }
153:
154: @Override
155: public Parameter<?> getParameter(String name) {
156: return query.getParameter(name);
157: }
158:
159: @Override
160: public Set<Parameter<?>> getParameters() {
161: return query.getParameters();
162: }
163:
164: @Override
165: public boolean isBound(Parameter<?> parameter) {
166: return query.getParameterValue(parameter) != null;
167: }
168:
169: @Override
170: public Object getParameterValue(String name) {
171: final Parameter<?> param = query.getParameter(name);
172: return getParameterValue(param);
173: }
174:
175: @Override
176: public Object getParameterValue(int position) {
177: final Parameter<?> param = query.getParameter(position);
178: return getParameterValue(param);
179: }
180:
181: @Override
182: public <T> T getParameterValue(Parameter<T> parameter) {
183: if (!isBound(parameter)) {
184: throw unboundParam(parameter);
185: }
186: return (T) query.getParameterValue(parameter);
187: }
188:
189: @Override
190: public Query setParameter(int position, Object value) {
191: ensureOpen();
192: try {
193: query.setParameter(query.getParameter(position), value);
194: } catch (RuntimeException e) {
195: markTransactionForRollback();
196: throw e;
197: }
198: return this;
199: }
200:
201: @Override
202: public Query setParameter(int position, String value, String language) {
203: ensureOpen();
204: try {
205: query.setParameter(query.getParameter(position), value, language);
206: } catch (RuntimeException e) {
207: markTransactionForRollback();
208: throw e;
209: }
210: return this;
211: }
212:
213: @Override
214: public Query setParameter(String name, Object value) {
215: ensureOpen();
216: try {
217: query.setParameter(query.getParameter(name), value);
218: } catch (RuntimeException e) {
219: markTransactionForRollback();
220: throw e;
221: }
222: return this;
223: }
224:
225: @Override
226: public Query setParameter(String name, String value, String language) {
227: ensureOpen();
228: try {
229: query.setParameter(query.getParameter(name), value, language);
230: } catch (RuntimeException e) {
231: markTransactionForRollback();
232: throw e;
233: }
234: return this;
235: }
236:
237: @Override
238: public Query setUntypedParameter(int position, Object value) {
239: ensureOpen();
240: try {
241: query.setUntypedParameter(query.getParameter(position), value);
242: } catch (RuntimeException e) {
243: markTransactionForRollback();
244: throw e;
245: }
246: return this;
247: }
248:
249: @Override
250: public Query setUntypedParameter(String name, Object value) {
251: ensureOpen();
252: try {
253: query.setUntypedParameter(query.getParameter(name), value);
254: } catch (RuntimeException e) {
255: markTransactionForRollback();
256: throw e;
257: }
258: return this;
259: }
260:
261: @Override
262: public int getMaxResults() {
263: return query.getMaxResults();
264: }
265:
266: @Override
267: public int getFirstResult() {
268: return query.getFirstResult();
269: }
270:
271: void checkNumericParameter(int param, String name) {
272: if (param < 0) {
273: markTransactionForRollback();
274: throw new IllegalArgumentException("Cannot set " + name + " to less than 0.");
275: }
276: }
277:
278: /**
279: * Executes the query and lets the specified consumer deal with each row in the result set.
280: *
281: * @param consumer Called for every row in the result set
282: * @throws OntoDriverException When something goes wrong during query evaluation or result set processing
283: */
284: void executeQuery(ThrowingConsumer<ResultRow, OntoDriverException> consumer) throws OntoDriverException {
285: try (final Statement stmt = connection.createStatement()) {
286: setTargetOntology(stmt);
287: logQuery();
288: final ResultSet rs = stmt.executeQuery(query.assembleQuery());
289: for (ResultRow row : rs) {
290: consumer.accept(row);
291: }
292: }
293: }
294:
295: <R> Stream<R> executeQueryForStream(Function<ResultRow, Optional<R>> function) throws OntoDriverException {
296: final Statement stmt = connection.createStatement();
297: setTargetOntology(stmt);
298: logQuery();
299: final ResultSet rs = stmt.executeQuery(query.assembleQuery());
300: return StreamSupport.stream(new QueryResultSpliterator<>(rs.spliterator(), function, () -> {
301: try {
302: stmt.close();
303: } catch (OntoDriverException e) {
304: markTransactionForRollback();
305: throw new OWLPersistenceException(e);
306: }
307: }), false);
308: }
309:
310: boolean exceptionCausesRollback(RuntimeException e) {
311:• return !(e instanceof NoUniqueResultException) && !(e instanceof NoResultException);
312: }
313:
314: @Override
315: public <T> Query setParameter(Parameter<T> parameter, T value) {
316: ensureOpen();
317: try {
318: query.setParameter(parameter, value);
319: } catch (RuntimeException e) {
320: markTransactionForRollback();
321: throw e;
322: }
323: return this;
324: }
325:
326: @Override
327: public Query setParameter(Parameter<String> parameter, String value, String language) {
328: ensureOpen();
329: try {
330: query.setParameter(parameter, value, language);
331: } catch (RuntimeException e) {
332: markTransactionForRollback();
333: throw e;
334: }
335: return this;
336: }
337:
338: @Override
339: public <T> Query setUntypedParameter(Parameter<T> parameter, T value) {
340: ensureOpen();
341: try {
342: query.setUntypedParameter(parameter, value);
343: } catch (RuntimeException e) {
344: markTransactionForRollback();
345: throw e;
346: }
347: return this;
348: }
349: }