Skip to content

Method: setTransactionWrapper()

1: /**
2: * Copyright (C) 2022 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.OWLPersistenceException;
18: import cz.cvut.kbss.jopa.exceptions.TransactionRequiredException;
19: import cz.cvut.kbss.jopa.model.annotations.CascadeType;
20: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
21: import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
22: import cz.cvut.kbss.jopa.model.metamodel.Attribute;
23: import cz.cvut.kbss.jopa.model.metamodel.Metamodel;
24: import cz.cvut.kbss.jopa.model.query.TypedQuery;
25: import cz.cvut.kbss.jopa.model.query.criteria.CriteriaQuery;
26: import cz.cvut.kbss.jopa.query.criteria.CriteriaParameterFiller;
27: import cz.cvut.kbss.jopa.sessions.CriteriaBuilder;
28: import cz.cvut.kbss.jopa.sessions.ServerSession;
29: import cz.cvut.kbss.jopa.sessions.UnitOfWorkImpl;
30: import cz.cvut.kbss.jopa.transactions.EntityTransaction;
31: import cz.cvut.kbss.jopa.transactions.EntityTransactionWrapper;
32: import cz.cvut.kbss.jopa.transactions.TransactionWrapper;
33: import cz.cvut.kbss.jopa.utils.*;
34: import org.slf4j.Logger;
35: import org.slf4j.LoggerFactory;
36:
37: import java.net.URI;
38: import java.util.*;
39:
40: public class EntityManagerImpl implements AbstractEntityManager, Wrapper {
41:
42: private static final Logger LOG = LoggerFactory.getLogger(EntityManagerImpl.class);
43:
44: private static final Object MAP_VALUE = new Object();
45:
46: private final EntityManagerFactoryImpl emf;
47: private final ServerSession serverSession;
48:
49: private boolean open;
50:
51: private TransactionWrapper transaction;
52: private UnitOfWorkImpl persistenceContext;
53: private final Configuration configuration;
54:
55: private Map<Object, Object> cascadingRegistry = new IdentityHashMap<>();
56:
57: EntityManagerImpl(EntityManagerFactoryImpl emf, Configuration configuration, ServerSession serverSession) {
58: this.emf = emf;
59: this.serverSession = serverSession;
60: this.configuration = configuration;
61:
62: setTransactionWrapper();
63:
64: this.open = true;
65: }
66:
67: public enum State {
68: MANAGED, MANAGED_NEW, NOT_MANAGED, REMOVED
69: }
70:
71: @Override
72: public void persist(final Object entity) {
73: final Descriptor d = new EntityDescriptor();
74: persist(entity, d);
75: }
76:
77: @Override
78: public void persist(final Object entity, final Descriptor descriptor) {
79: LOG.trace("Persisting instance of type {}.", entity.getClass());
80: try {
81: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
82: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
83: ensureOpen();
84: checkClassIsValidEntity(entity.getClass());
85:
86: switch (getState(entity, descriptor)) {
87: case NOT_MANAGED:
88: getCurrentPersistenceContext().registerNewObject(entity, descriptor);
89: // Intentional fall-through
90: case MANAGED:
91: cascadePersist(entity, descriptor);
92: break;
93: case REMOVED:
94: getCurrentPersistenceContext().restoreRemovedObject(entity);
95: break;
96: default:
97: break;
98: }
99: } catch (RuntimeException e) {
100: markTransactionForRollback();
101: throw e;
102: }
103: }
104:
105: private void checkClassIsValidEntity(Class<?> cls) {
106: getMetamodel().entity(cls);
107: }
108:
109: private void registerProcessedInstance(Object instance) {
110: cascadingRegistry.put(instance, MAP_VALUE);
111: }
112:
113: private boolean isCascadingCycle(Object instance) {
114: return cascadingRegistry.containsKey(instance);
115: }
116:
117: private void resetCascadingRegistry() {
118: cascadingRegistry.clear();
119: }
120:
121: private void markTransactionForRollback() {
122: if (getTransaction().isActive()) {
123: getTransaction().setRollbackOnly();
124: }
125: }
126:
127: private void cascadePersist(final Object entity, final Descriptor descriptor) {
128: new OneLevelCascadeExplorer() {
129: @Override
130: protected void exploreCascaded(Attribute<?, ?> at, Object o) {
131: try {
132: Object ox = EntityPropertiesUtils.getAttributeValue(at, o);
133: LOG.trace("object={}, attribute={}, value={}", o, at.getName(), ox);
134: if (ox == null) {
135: return;
136: }
137: final Descriptor attDescriptor = descriptor.getAttributeDescriptor(at);
138: if (at.isCollection()) {
139: for (final Object ox2 : (Collection<?>) ox) {
140: persist(ox2, attDescriptor);
141: }
142: } else {
143: persist(ox, attDescriptor);
144: }
145: } catch (Exception e) {
146: markTransactionForRollback();
147: throw new OWLPersistenceException(
148: "A problem occurred when persisting attribute " + at.getName()
149: + " of with value " + o + " of object " + entity, e);
150: }
151: }
152: }.start(this, entity, CascadeType.PERSIST);
153: }
154:
155: @Override
156: public <T> T merge(final T entity) {
157: final Descriptor d = new EntityDescriptor();
158: return merge(entity, d);
159: }
160:
161: @Override
162: public <T> T merge(final T entity, final Descriptor descriptor) {
163: try {
164: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
165: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
166: ensureOpen();
167: checkClassIsValidEntity(entity.getClass());
168:
169: return mergeInternal(entity, descriptor);
170: } catch (RuntimeException e) {
171: markTransactionForRollback();
172: throw e;
173: } finally {
174: resetCascadingRegistry();
175: }
176: }
177:
178: /**
179: * Merges state of the specified entity into the current persistence context. </p>
180: *
181: * @param entity Entity instance
182: * @param descriptor Contains information about contexts into which the entity and its field should be merged
183: * @return Managed instance of the merged entity
184: */
185: private <T> T mergeInternal(final T entity, final Descriptor descriptor) {
186: assert entity != null;
187: assert descriptor != null;
188: LOG.trace("Merging instance of type {}.", entity.getClass());
189: if (isCascadingCycle(entity)) {
190: LOG.warn("Merge cascading cycle detected in entity {}.", entity);
191: return (T) getCurrentPersistenceContext().getCloneForOriginal(entity);
192: }
193:
194: switch (getState(entity, descriptor)) {
195: case MANAGED_NEW:
196: case MANAGED:
197: registerProcessedInstance(entity);
198: cascadeMerge(entity, entity, descriptor);
199: return entity;
200: case NOT_MANAGED:
201: final T merged = getCurrentPersistenceContext().mergeDetached(entity, descriptor);
202: registerProcessedInstance(entity);
203: cascadeMerge(merged, entity, descriptor);
204: return merged;
205: case REMOVED:
206: default:
207: throw new IllegalArgumentException("Cannot merge instance which is not an entity or is removed.");
208: }
209: }
210:
211: private <T> void cascadeMerge(T merged, T toMerge, Descriptor descriptor) {
212: new OneLevelMergeCascadeExplorer() {
213: @Override
214: protected void exploreCascaded(Attribute<?, ?> at, Object merged, Object toMerge) {
215: final Descriptor attDescriptor = descriptor.getAttributeDescriptor(at);
216: mergeX(at, merged, toMerge, attDescriptor);
217: }
218: }.start(this, merged, toMerge);
219: }
220:
221: private void mergeX(Attribute<?, ?> at, Object merged, Object toMerge, Descriptor descriptor) {
222: Object attVal = EntityPropertiesUtils.getAttributeValue(at, toMerge);
223: if (attVal == null) {
224: return;
225: }
226: if (at.isCollection()) {
227: Collection c = (Collection) attVal;
228: Collection result = CollectionFactory.createInstance(c);
229: for (final Object ox2 : c) {
230: result.add(mergeInternal(ox2, descriptor));
231: }
232: attVal = getCurrentPersistenceContext().createIndirectCollection(result, merged, at.getJavaField());
233: } else {
234: attVal = mergeInternal(attVal, descriptor);
235: }
236: EntityPropertiesUtils.setFieldValue(at.getJavaField(), merged, attVal);
237: }
238:
239: @Override
240: public void remove(Object object) {
241: try {
242: ensureOpen();
243: Objects.requireNonNull(object);
244: checkClassIsValidEntity(object.getClass());
245: if (isCascadingCycle(object)) {
246: LOG.warn("Remove cascading cycle detected in instance {}.", object);
247: return;
248: }
249:
250: switch (getState(object)) {
251: case MANAGED_NEW:
252: case MANAGED:
253: getCurrentPersistenceContext().removeObject(object);
254: registerProcessedInstance(object);
255: // Intentional fall-through
256: case REMOVED:
257: new SimpleOneLevelCascadeExplorer(this::remove).start(this, object, CascadeType.REMOVE);
258: break;
259: default:
260: throw new IllegalArgumentException("Entity " + object + " is not managed and cannot be removed.");
261: }
262: } catch (RuntimeException e) {
263: markTransactionForRollback();
264: throw e;
265: } finally {
266: resetCascadingRegistry();
267: }
268: }
269:
270: @Override
271: public <T> T find(Class<T> cls, Object identifier) {
272: final EntityDescriptor d = new EntityDescriptor();
273: return find(cls, identifier, d);
274: }
275:
276: @Override
277: public <T> T find(Class<T> cls, Object identifier, Descriptor descriptor) {
278: try {
279: Objects.requireNonNull(cls, ErrorUtils.getNPXMessageSupplier("cls"));
280: Objects.requireNonNull(identifier, ErrorUtils.getNPXMessageSupplier("primaryKey"));
281: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
282: ensureOpen();
283: checkClassIsValidEntity(cls);
284:
285: LOG.trace("Finding instance of {} with identifier {} in context {}.", cls, identifier, descriptor);
286: final URI uri = (identifier instanceof URI) ? (URI) identifier : URI.create(identifier.toString());
287:
288: return getCurrentPersistenceContext().readObject(cls, uri, descriptor);
289: } catch (RuntimeException e) {
290: markTransactionForRollback();
291: throw e;
292: }
293: }
294:
295: @Override
296: public <T> T getReference(Class<T> entityClass, Object identifier) {
297: try {
298: Objects.requireNonNull(entityClass);
299: Objects.requireNonNull(identifier);
300:
301: return getReference(entityClass, identifier, new EntityDescriptor());
302: } catch (RuntimeException e) {
303: markTransactionForRollback();
304: throw e;
305: }
306: }
307:
308: @Override
309: public <T> T getReference(Class<T> entityClass, Object identifier, Descriptor descriptor) {
310: try {
311: Objects.requireNonNull(entityClass);
312: Objects.requireNonNull(identifier);
313: Objects.requireNonNull(descriptor);
314: ensureOpen();
315: checkClassIsValidEntity(entityClass);
316:
317: LOG.trace("Getting reference of type {} with identifier {} in context {}.", entityClass, identifier,
318: descriptor);
319: return getCurrentPersistenceContext().getReference(entityClass, identifier, descriptor);
320: } catch (RuntimeException e) {
321: markTransactionForRollback();
322: throw e;
323: }
324: }
325:
326: @Override
327: public void flush() {
328: try {
329: ensureOpen();
330: LOG.trace("Flushing changes...");
331: if (!getTransaction().isActive()) {
332: throw new TransactionRequiredException("Cannot flush entity manager outside of a transaction.");
333: }
334: this.getCurrentPersistenceContext().writeUncommittedChanges();
335: } catch (RuntimeException e) {
336: markTransactionForRollback();
337: throw e;
338: }
339: }
340:
341: @Override
342: public void refresh(Object entity) {
343: try {
344: ensureOpen();
345: Objects.requireNonNull(entity);
346: checkClassIsValidEntity(entity.getClass());
347:
348: this.getCurrentPersistenceContext().refreshObject(entity);
349: new SimpleOneLevelCascadeExplorer(this::refresh).start(this, entity, CascadeType.REFRESH);
350: } catch (RuntimeException e) {
351: markTransactionForRollback();
352: throw e;
353: }
354: }
355:
356: @Override
357: public void clear() {
358: try {
359: ensureOpen();
360: getCurrentPersistenceContext().clear();
361: } catch (RuntimeException e) {
362: markTransactionForRollback();
363: throw e;
364: }
365: }
366:
367: @Override
368: public void detach(Object entity) {
369: try {
370: ensureOpen();
371:
372: switch (getState(entity)) {
373: case MANAGED_NEW:
374: case MANAGED:
375: getCurrentPersistenceContext().unregisterObject(entity);
376: new SimpleOneLevelCascadeExplorer(this::detach).start(this, entity, CascadeType.DETACH);
377: break;
378: default:
379: break;
380: }
381: } catch (RuntimeException e) {
382: markTransactionForRollback();
383: throw e;
384: }
385: }
386:
387: @Override
388: public boolean contains(Object entity) {
389: try {
390: ensureOpen();
391: Objects.requireNonNull(entity);
392: checkClassIsValidEntity(entity.getClass());
393: return getCurrentPersistenceContext().contains(entity);
394: } catch (RuntimeException e) {
395: markTransactionForRollback();
396: throw e;
397: }
398: }
399:
400: @Override
401: public void close() {
402: ensureOpen();
403: removeCurrentPersistenceContext();
404: this.cascadingRegistry = null;
405: emf.entityManagerClosed(this);
406: this.open = false;
407: }
408:
409: @Override
410: public boolean isOpen() {
411: return open;
412: }
413:
414: @Override
415: public EntityTransaction getTransaction() {
416: return transaction.getTransaction();
417: }
418:
419: @Override
420: public EntityManagerFactoryImpl getEntityManagerFactory() {
421: return emf;
422: }
423:
424: @Override
425: public Metamodel getMetamodel() {
426: return emf.getMetamodel();
427: }
428:
429: @Override
430: public boolean isLoaded(final Object object, final String attributeName) {
431: Objects.requireNonNull(object);
432: Objects.requireNonNull(attributeName);
433: return getCurrentPersistenceContext().isLoaded(object, attributeName) == LoadState.LOADED;
434: }
435:
436: @Override
437: public boolean isLoaded(Object object) {
438: return getCurrentPersistenceContext().isLoaded(object) == LoadState.LOADED;
439: }
440:
441: @Override
442: public QueryImpl createQuery(String qlString) {
443: ensureOpen();
444: final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createQuery(qlString);
445: q.setRollbackOnlyMarker(this::markTransactionForRollback);
446: q.setEnsureOpenProcedure(this::ensureOpen);
447: return q;
448: }
449:
450: @Override
451: public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
452: ensureOpen();
453: CriteriaQueryImpl<T> query = (CriteriaQueryImpl<T>) criteriaQuery;
454: CriteriaParameterFiller parameterFiller = new CriteriaParameterFiller();
455: String soqlQuery = query.translateQuery(parameterFiller);
456: LOG.debug("CriteriaQuery translate to SOQL query: " + soqlQuery);
457: final TypedQueryImpl<T> q = getCurrentPersistenceContext().sparqlQueryFactory().createQuery(soqlQuery, query.getResultType());
458: q.setRollbackOnlyMarker(this::markTransactionForRollback);
459: q.setEnsureOpenProcedure(this::ensureOpen);
460: parameterFiller.setValuesToRegisteredParameters(q);
461:
462: return q;
463: }
464:
465: @Override
466: public <T> TypedQueryImpl<T> createQuery(String query, Class<T> resultClass) {
467: ensureOpen();
468: final TypedQueryImpl<T> q = getCurrentPersistenceContext().sparqlQueryFactory().createQuery(query, resultClass);
469: q.setRollbackOnlyMarker(this::markTransactionForRollback);
470: q.setEnsureOpenProcedure(this::ensureOpen);
471: return q;
472: }
473:
474: @Override
475: public QueryImpl createNativeQuery(String sparqlString) {
476: ensureOpen();
477: final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createNativeQuery(sparqlString);
478: q.setRollbackOnlyMarker(this::markTransactionForRollback);
479: q.setEnsureOpenProcedure(this::ensureOpen);
480: return q;
481: }
482:
483: @Override
484: public <T> TypedQueryImpl<T> createNativeQuery(String sparqlString, Class<T> resultClass) {
485: ensureOpen();
486: final TypedQueryImpl<T> q = getCurrentPersistenceContext().sparqlQueryFactory()
487: .createNativeQuery(sparqlString, resultClass);
488: q.setRollbackOnlyMarker(this::markTransactionForRollback);
489: q.setEnsureOpenProcedure(this::ensureOpen);
490: return q;
491: }
492:
493: @Override
494: public QueryImpl createNativeQuery(String sparqlString, String resultSetMapping) {
495: ensureOpen();
496: final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory()
497: .createNativeQuery(sparqlString, resultSetMapping);
498: q.setRollbackOnlyMarker(this::markTransactionForRollback);
499: q.setEnsureOpenProcedure(this::ensureOpen);
500: return q;
501: }
502:
503: @Override
504: public QueryImpl createNamedQuery(String name) {
505: ensureOpen();
506: final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createNamedQuery(name);
507: q.setRollbackOnlyMarker(this::markTransactionForRollback);
508: q.setEnsureOpenProcedure(this::ensureOpen);
509: return q;
510: }
511:
512: @Override
513: public <T> TypedQueryImpl<T> createNamedQuery(String name, Class<T> resultClass) {
514: ensureOpen();
515: final TypedQueryImpl<T> q = getCurrentPersistenceContext().sparqlQueryFactory()
516: .createNamedQuery(name, resultClass);
517: q.setRollbackOnlyMarker(this::markTransactionForRollback);
518: q.setEnsureOpenProcedure(this::ensureOpen);
519: return q;
520: }
521:
522: @Override
523: public boolean isConsistent(URI context) {
524: ensureOpen();
525: return getCurrentPersistenceContext().isConsistent(context);
526: }
527:
528: @Override
529: public List<URI> getContexts() {
530: ensureOpen();
531: return getCurrentPersistenceContext().getContexts();
532: }
533:
534: @Override
535: public CriteriaBuilder getCriteriaBuilder() {
536: return getCurrentPersistenceContext().criteriaFactory();
537: }
538:
539: @Override
540: public void setUseTransactionalOntologyForQueryProcessing() {
541: ensureOpen();
542: getCurrentPersistenceContext().setUseTransactionalOntologyForQueryProcessing();
543: }
544:
545: @Override
546: public boolean useTransactionalOntologyForQueryProcessing() {
547: ensureOpen();
548: return getCurrentPersistenceContext().useTransactionalOntologyForQueryProcessing();
549: }
550:
551: @Override
552: public void setUseBackupOntologyForQueryProcessing() {
553: ensureOpen();
554: getCurrentPersistenceContext().setUseBackupOntologyForQueryProcessing();
555: }
556:
557: @Override
558: public boolean useBackupOntologyForQueryProcessing() {
559: ensureOpen();
560: return getCurrentPersistenceContext().useBackupOntologyForQueryProcessing();
561: }
562:
563: @Override
564: public <T> T unwrap(Class<T> cls) {
565: ensureOpen();
566: if (cls.isAssignableFrom(this.getClass())) {
567: return cls.cast(this);
568: }
569: return getCurrentPersistenceContext().unwrap(cls);
570: }
571:
572: @Override
573: public Object getDelegate() {
574: return unwrap(EntityManagerImpl.class);
575: }
576:
577: private void ensureOpen() {
578: if (!isOpen()) {
579: throw new IllegalStateException("The entity manager is closed !");
580: }
581: }
582:
583: private State getState(Object entity) {
584: return getCurrentPersistenceContext().getState(entity);
585: }
586:
587: private State getState(Object entity, Descriptor descriptor) {
588: return getCurrentPersistenceContext().getState(entity, descriptor);
589: }
590:
591: @Override
592: protected void finalize() throws Throwable {
593: if (open) {
594: close();
595: }
596: super.finalize();
597: }
598:
599: @Override
600: public UnitOfWorkImpl getCurrentPersistenceContext() {
601: if (this.persistenceContext == null) {
602: this.persistenceContext = (UnitOfWorkImpl) serverSession.acquireUnitOfWork();
603: persistenceContext.setEntityManager(this);
604: }
605: return this.persistenceContext;
606: }
607:
608: /**
609: * Called from EntityTransaction in case of a rollback. Releasing the UoW is up to the EntityTransaction.
610: */
611: @Override
612: public void removeCurrentPersistenceContext() {
613: if (persistenceContext != null && persistenceContext.isActive()) {
614: persistenceContext.release();
615: }
616: this.persistenceContext = null;
617: }
618:
619: @Override
620: public void transactionStarted(EntityTransaction t) {
621: serverSession.transactionStarted(t, this);
622: }
623:
624: @Override
625: public void transactionFinished(EntityTransaction t) {
626: this.serverSession.transactionFinished(t);
627: }
628:
629: /**
630: * Since we support only EntityTransactions, we set the TransactionWrapper to EntityTransactionWrapper.
631: * <p>
632: * In the future, if JTA transactions are supported, JTATransactionWrapper should be set instead of the
633: * EntityTransactionWrapper.
634: */
635: private void setTransactionWrapper() {
636: this.transaction = new EntityTransactionWrapper(this);
637: }
638:
639: @Override
640: public Configuration getConfiguration() {
641: return configuration;
642: }
643:
644: @Override
645: public Map<String, Object> getProperties() {
646: return new HashMap<>(configuration.getProperties());
647: }
648:
649: @Override
650: public void setProperty(String propertyName, Object value) {
651: Objects.requireNonNull(propertyName);
652: Objects.requireNonNull(value);
653: configuration.set(propertyName, value.toString());
654: }
655: }