
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}