
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}