001/*-
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2024 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         *
100         * @since 7.4.0
101         */
102        @Beta
103        default boolean isCompatiblePartition(
104                        RequestPartitionId theRequestPartitionId, RequestPartitionId theOtherRequestPartitionId) {
105                return true;
106        }
107
108        interface IExecutionBuilder extends TransactionOperations {
109
110                IExecutionBuilder withIsolation(Isolation theIsolation);
111
112                IExecutionBuilder withTransactionDetails(TransactionDetails theTransactionDetails);
113
114                IExecutionBuilder withPropagation(Propagation thePropagation);
115
116                IExecutionBuilder withRequestPartitionId(RequestPartitionId theRequestPartitionId);
117
118                IExecutionBuilder readOnly();
119
120                IExecutionBuilder onRollback(Runnable theOnRollback);
121
122                void execute(Runnable theTask);
123
124                <T> T execute(Callable<T> theTask);
125
126                <T> T execute(@Nonnull TransactionCallback<T> callback);
127
128                /**
129                 * Read query path.
130                 */
131                default <T> T read(Callable<T> theCallback) {
132                        return execute(theCallback);
133                }
134
135                /**
136                 * Search for open Stream.
137                 * The Stream may not be readable outside an outermost transaction.
138                 */
139                default <T> Stream<T> search(Callable<Stream<T>> theCallback) {
140                        return execute(theCallback);
141                }
142
143                /**
144                 * Search for concrete List.
145                 */
146                default <T> List<T> searchList(Callable<List<T>> theCallback) {
147                        return execute(theCallback);
148                }
149        }
150}