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