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        private JpaPid(Long theId, String theResourceType) {
098                super(theResourceType);
099                myId = theId;
100        }
101
102        private JpaPid(Long theId, Long theVersion, String theResourceType) {
103                super(theVersion, theResourceType);
104                myId = theId;
105        }
106
107        public PartitionablePartitionId getPartitionablePartitionId() {
108                return new PartitionablePartitionId(myPartitionIdValue, null);
109        }
110
111        public JpaPid setPartitionablePartitionId(PartitionablePartitionId thePartitionablePartitionId) {
112                myPartitionIdValue = thePartitionablePartitionId != null ? thePartitionablePartitionId.getPartitionId() : null;
113                return this;
114        }
115
116        public JpaPid setPartitionIdIfNotAlreadySet(Integer thePartitionId) {
117                if (myPartitionIdValue == null && thePartitionId != null) {
118                        myPartitionIdValue = thePartitionId;
119                }
120                return this;
121        }
122
123        @Override
124        public Integer getPartitionId() {
125                return myPartitionIdValue;
126        }
127
128        public void setPartitionId(Integer thePartitionId) {
129                myPartitionIdValue = thePartitionId;
130        }
131
132        /**
133         * Note that equals and hashCode for this object only consider the ID and Partition ID because
134         * this class gets used as cache keys
135         */
136        @Override
137        public boolean equals(Object theO) {
138                if (this == theO) {
139                        return true;
140                }
141                if (!(theO instanceof JpaPid)) {
142                        return false;
143                }
144                JpaPid jpaPid = (JpaPid) theO;
145                return Objects.equals(myId, jpaPid.myId);
146        }
147
148        /**
149         * Note that equals and hashCode for this object only consider the ID and Partition ID because
150         * this class gets used as cache keys
151         */
152        @Override
153        public int hashCode() {
154                return Objects.hash(myId);
155        }
156
157        @Override
158        public Long getId() {
159                return myId;
160        }
161
162        public void setId(Long theId) {
163                myId = theId;
164        }
165
166        @Override
167        public String toString() {
168                String retVal = myPartitionIdValue != null ? myPartitionIdValue + "/" + myId.toString() : myId.toString();
169                return retVal;
170        }
171
172        @Override
173        public int compareTo(@Nonnull JpaPid theOther) {
174                return COMPARATOR.compare(this, theOther);
175        }
176
177        public JpaPidFk toFk() {
178                return JpaPidFk.fromPid(this);
179        }
180
181        public static List<Long> toLongList(JpaPid[] thePids) {
182                return toLongList(Arrays.asList(thePids));
183        }
184
185        public static List<Long> toLongList(Collection<JpaPid> thePids) {
186                List<Long> retVal = new ArrayList<>(thePids.size());
187                for (JpaPid next : thePids) {
188                        retVal.add(next.getId());
189                }
190                return retVal;
191        }
192
193        public static Set<Long> toLongSet(Collection<JpaPid> thePids) {
194                Set<Long> retVal = new HashSet<>(thePids.size());
195                for (JpaPid next : thePids) {
196                        retVal.add(next.getId());
197                }
198                return retVal;
199        }
200
201        public static List<JpaPid> fromLongList(Collection<Long> theResultList) {
202                List<JpaPid> retVal = new ArrayList<>(theResultList.size());
203                for (Long next : theResultList) {
204                        retVal.add(fromId(next));
205                }
206                return retVal;
207        }
208
209        public static JpaPid fromId(Long theId) {
210                return new JpaPid(theId);
211        }
212
213        public static JpaPid fromId(Long theResourceId, Integer thePartitionId) {
214                return new JpaPid(thePartitionId, theResourceId);
215        }
216
217        public static JpaPid fromId(Long theResourceId, PartitionablePartitionId thePartitionId) {
218                return new JpaPid(thePartitionId != null ? thePartitionId.getPartitionId() : null, theResourceId);
219        }
220
221        public static JpaPid fromIdAndVersion(Long theId, Long theVersion) {
222                return new JpaPid(theId, theVersion);
223        }
224
225        public static JpaPid fromIdAndResourceType(Long theId, String theResourceType) {
226                return new JpaPid(theId, theResourceType);
227        }
228
229        public static JpaPid fromIdAndVersionAndResourceType(Long theId, Long theVersion, String theResourceType) {
230                return new JpaPid(theId, theVersion, theResourceType);
231        }
232
233        public static JpaPid fromId(IdAndPartitionId theId) {
234                JpaPid retVal = new JpaPid(theId.getId());
235                retVal.setPartitionIdIfNotAlreadySet(theId.getPartitionIdValue());
236                return retVal;
237        }
238}