001/*-
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.dao.tx;
021
022import ca.uhn.fhir.interceptor.model.RequestPartitionId;
023import ca.uhn.fhir.rest.api.server.RequestDetails;
024import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
025import ca.uhn.fhir.util.ICallable;
026import com.google.common.annotations.Beta;
027import jakarta.annotation.Nonnull;
028import jakarta.annotation.Nullable;
029import org.springframework.transaction.annotation.Isolation;
030import org.springframework.transaction.annotation.Propagation;
031import org.springframework.transaction.support.TransactionCallback;
032import org.springframework.transaction.support.TransactionOperations;
033
034import java.util.List;
035import java.util.concurrent.Callable;
036import java.util.stream.Stream;
037
038/**
039 * This class is used to execute code within the context of a database transaction,
040 * just like Spring's {@link org.springframework.transaction.support.TransactionTemplate}
041 * but with more functionality. It can auto-execute code upon rollback, it translates
042 * specific exceptions, and it stores transaction context in a ThreadLocal.
043 */
044public interface IHapiTransactionService {
045
046        /**
047         * Fluent builder for creating a transactional callback
048         * <p>
049         * Method chain must end with a call to {@link IExecutionBuilder#execute(Runnable)} or one of the other
050         * overloads of <code>task(...)</code>
051         * </p>
052         */
053        IExecutionBuilder withRequest(@Nullable RequestDetails theRequestDetails);
054
055        /**
056         * Fluent builder for internal system requests with no external
057         * requestdetails associated
058         */
059        IExecutionBuilder withSystemRequest();
060
061        /**
062         * Fluent builder for internal system requests with no external
063         * {@link RequestDetails} associated and a pre-specified partition ID.
064         * This method is sugar for
065         * <pre>
066         *    withSystemRequest()
067         *                      .withRequestPartitionId(thePartitionId);
068         * </pre>
069         *
070         * @since 6.6.0
071         */
072        default IExecutionBuilder withSystemRequestOnPartition(RequestPartitionId theRequestPartitionId) {
073                return withSystemRequest().withRequestPartitionId(theRequestPartitionId);
074        }
075
076        /**
077         * Convenience for TX working with non-partitioned entities.
078         */
079        default IExecutionBuilder withSystemRequestOnDefaultPartition() {
080                return withSystemRequestOnPartition(RequestPartitionId.defaultPartition());
081        }
082
083        /**
084         * @deprecated It is highly recommended to use {@link #withRequest(RequestDetails)} instead of this method, for increased visibility.
085         */
086        @Deprecated(since = "6.10")
087        <T> T withRequest(
088                        @Nullable RequestDetails theRequestDetails,
089                        @Nullable TransactionDetails theTransactionDetails,
090                        @Nonnull Propagation thePropagation,
091                        @Nonnull Isolation theIsolation,
092                        @Nonnull ICallable<T> theCallback);
093
094        /**
095         * Are two RequestPartitionId values compatible within the same transaction?
096         * <p>
097         * This is an experimental API, subject to change in a future release.
098         * </p>
099         * <p>
100         * This method should return the same result regardless of the order of the arguments.
101         * </p>
102         *
103         * @since 7.4.0
104         */
105        @Beta
106        default boolean isCompatiblePartition(
107                        RequestPartitionId theRequestPartitionId, RequestPartitionId theOtherRequestPartitionId) {
108                return true;
109        }
110
111        interface IExecutionBuilder extends TransactionOperations {
112
113                IExecutionBuilder withIsolation(Isolation theIsolation);
114
115                IExecutionBuilder withTransactionDetails(TransactionDetails theTransactionDetails);
116
117                IExecutionBuilder withPropagation(Propagation thePropagation);
118
119                IExecutionBuilder withRequestPartitionId(RequestPartitionId theRequestPartitionId);
120
121                /**
122                 * Mark the transaction as read-only.
123                 */
124                IExecutionBuilder readOnly();
125
126                /**
127                 * Mark the transaction as read-only, if {@literal theReadOnly} is true.
128                 *
129                 * @since 8.6.0
130                 */
131                default IExecutionBuilder readOnly(boolean theReadOnly) {
132                        if (theReadOnly) {
133                                return readOnly();
134                        } else {
135                                return this;
136                        }
137                }
138
139                /**
140                 * @deprecated Use {@link TransactionDetails#addRollbackUndoAction(Runnable)} instead
141                 */
142                @Deprecated(since = "8.6", forRemoval = true)
143                IExecutionBuilder onRollback(Runnable theOnRollback);
144
145                void execute(Runnable theTask);
146
147                <T> T execute(Callable<T> theTask);
148
149                <T> T execute(@Nonnull TransactionCallback<T> callback);
150
151                /**
152                 * Read query path.
153                 */
154                <T> T read(IExecutionCallable<T> theCallback);
155
156                /**
157                 * Search for open Stream.
158                 * The Stream may not be readable outside an outermost transaction.
159                 */
160                <T> Stream<T> search(IExecutionCallable<Stream<T>> theCallback);
161
162                /**
163                 * Search for concrete List.
164                 */
165                <T> List<T> searchList(IExecutionCallable<List<T>> theCallback);
166        }
167
168        interface IExecutionCallable<T> {
169
170                T call(RequestPartitionId theRequestPartition);
171        }
172}