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