
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}