001/*-
002 * #%L
003 * HAPI FHIR JPA Model
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.model.dao;
021
022import ca.uhn.fhir.jpa.model.entity.IdAndPartitionId;
023import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
024import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId;
025import ca.uhn.hapi.fhir.sql.hibernatesvc.PartitionedIdProperty;
026import jakarta.annotation.Nonnull;
027import jakarta.persistence.Column;
028import jakarta.persistence.Embeddable;
029import jakarta.persistence.GeneratedValue;
030import jakarta.persistence.GenerationType;
031import org.apache.commons.collections4.ComparatorUtils;
032import org.hibernate.annotations.GenericGenerator;
033import org.hibernate.search.engine.backend.types.Projectable;
034import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
035
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.Collection;
039import java.util.Comparator;
040import java.util.HashSet;
041import java.util.List;
042import java.util.Objects;
043import java.util.Set;
044
045import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
046
047/**
048 * JPA implementation of IResourcePersistentId.  JPA uses a Long as the primary key.  This class should be used in any
049 * context where the pid is known to be a Long.
050 */
051@Embeddable
052public class JpaPid extends BaseResourcePersistentId<Long> implements Comparable<JpaPid> {
053
054        @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class)
055        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID")
056        @Column(name = "RES_ID", nullable = false)
057        @GenericField(projectable = Projectable.YES)
058        private Long myId;
059
060        @PartitionedIdProperty
061        @Column(name = PartitionablePartitionId.PARTITION_ID, nullable = false)
062        private Integer myPartitionIdValue;
063
064        private static final Comparator<JpaPid> COMPARATOR;
065
066        static {
067                Comparator<JpaPid> partitionComparator =
068                                Comparator.comparing(t -> defaultIfNull(t.myPartitionIdValue, Integer.MIN_VALUE));
069                Comparator<JpaPid> idComparator = Comparator.comparing(t -> t.myId);
070                COMPARATOR = ComparatorUtils.chainedComparator(List.of(partitionComparator, idComparator));
071        }
072
073        /**
074         * Constructor - Do not call this directly, only used for
075         * JPA instantiation
076         */
077        public JpaPid() {
078                super(null);
079        }
080
081        private JpaPid(Long theId) {
082                super(null);
083                myId = theId;
084        }
085
086        public JpaPid(Integer thePartitionIdValue, Long theId) {
087                super(null);
088                myId = theId;
089                myPartitionIdValue = thePartitionIdValue;
090        }
091
092        private JpaPid(Long theId, Long theVersion) {
093                super(theVersion, null);
094                myId = theId;
095        }
096
097        public JpaPid(Integer thePartitionId, Long theId, Long theVersion) {
098                super(theVersion, null);
099                myPartitionIdValue = thePartitionId;
100                myId = theId;
101        }
102
103        private JpaPid(Long theId, String theResourceType) {
104                super(theResourceType);
105                myId = theId;
106        }
107
108        private JpaPid(Long theId, Long theVersion, String theResourceType) {
109                super(theVersion, theResourceType);
110                myId = theId;
111        }
112
113        public PartitionablePartitionId getPartitionablePartitionId() {
114                return new PartitionablePartitionId(myPartitionIdValue, null);
115        }
116
117        public JpaPid setPartitionablePartitionId(PartitionablePartitionId thePartitionablePartitionId) {
118                myPartitionIdValue = thePartitionablePartitionId != null ? thePartitionablePartitionId.getPartitionId() : null;
119                return this;
120        }
121
122        public JpaPid setPartitionIdIfNotAlreadySet(Integer thePartitionId) {
123                if (myPartitionIdValue == null && thePartitionId != null) {
124                        myPartitionIdValue = thePartitionId;
125                }
126                return this;
127        }
128
129        @Override
130        public Integer getPartitionId() {
131                return myPartitionIdValue;
132        }
133
134        public void setPartitionId(Integer thePartitionId) {
135                myPartitionIdValue = thePartitionId;
136        }
137
138        /**
139         * Note that equals and hashCode for this object only consider the ID and Partition ID because
140         * this class gets used as cache keys
141         */
142        @Override
143        public boolean equals(Object theO) {
144                if (this == theO) {
145                        return true;
146                }
147                if (!(theO instanceof JpaPid)) {
148                        return false;
149                }
150                JpaPid jpaPid = (JpaPid) theO;
151                return Objects.equals(myId, jpaPid.myId);
152        }
153
154        /**
155         * Note that equals and hashCode for this object only consider the ID and Partition ID because
156         * this class gets used as cache keys
157         */
158        @Override
159        public int hashCode() {
160                return Objects.hash(myId);
161        }
162
163        @Override
164        public Long getId() {
165                return myId;
166        }
167
168        public void setId(Long theId) {
169                myId = theId;
170        }
171
172        @Override
173        public String toString() {
174                String retVal = myPartitionIdValue != null ? myPartitionIdValue + "/" + myId.toString() : myId.toString();
175                return retVal;
176        }
177
178        @Override
179        public int compareTo(@Nonnull JpaPid theOther) {
180                return COMPARATOR.compare(this, theOther);
181        }
182
183        public JpaPidFk toFk() {
184                return JpaPidFk.fromPid(this);
185        }
186
187        public static List<Long> toLongList(JpaPid[] thePids) {
188                return toLongList(Arrays.asList(thePids));
189        }
190
191        public static List<Long> toLongList(Collection<JpaPid> thePids) {
192                List<Long> retVal = new ArrayList<>(thePids.size());
193                for (JpaPid next : thePids) {
194                        retVal.add(next.getId());
195                }
196                return retVal;
197        }
198
199        public static Set<Long> toLongSet(Collection<JpaPid> thePids) {
200                Set<Long> retVal = new HashSet<>(thePids.size());
201                for (JpaPid next : thePids) {
202                        retVal.add(next.getId());
203                }
204                return retVal;
205        }
206
207        public static List<JpaPid> fromLongList(Collection<Long> theResultList) {
208                List<JpaPid> retVal = new ArrayList<>(theResultList.size());
209                for (Long next : theResultList) {
210                        retVal.add(fromId(next));
211                }
212                return retVal;
213        }
214
215        public static JpaPid fromId(Long theId) {
216                return new JpaPid(theId);
217        }
218
219        public static JpaPid fromId(Long theResourceId, Integer thePartitionId) {
220                return new JpaPid(thePartitionId, theResourceId);
221        }
222
223        public static JpaPid fromId(Long theResourceId, PartitionablePartitionId thePartitionId) {
224                return new JpaPid(thePartitionId != null ? thePartitionId.getPartitionId() : null, theResourceId);
225        }
226
227        public static JpaPid fromIdAndVersion(Long theId, Long theVersion) {
228                return new JpaPid(theId, theVersion);
229        }
230
231        public static JpaPid fromIdAndResourceType(Long theId, String theResourceType) {
232                return new JpaPid(theId, theResourceType);
233        }
234
235        public static JpaPid fromIdAndVersionAndResourceType(Long theId, Long theVersion, String theResourceType) {
236                return new JpaPid(theId, theVersion, theResourceType);
237        }
238
239        public static JpaPid fromId(IdAndPartitionId theId) {
240                JpaPid retVal = new JpaPid(theId.getId());
241                retVal.setPartitionIdIfNotAlreadySet(theId.getPartitionIdValue());
242                return retVal;
243        }
244}