001package ca.uhn.fhir.jpa.model.entity; 002 003/* 004 * #%L 005 * HAPI FHIR JPA Model 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; 025import ca.uhn.fhir.jpa.model.cross.IResourceLookup; 026import ca.uhn.fhir.jpa.model.search.ExtendedLuceneIndexData; 027import ca.uhn.fhir.jpa.model.search.ResourceTableRoutingBinder; 028import ca.uhn.fhir.jpa.model.search.SearchParamTextPropertyBinder; 029import ca.uhn.fhir.model.primitive.IdDt; 030import ca.uhn.fhir.rest.api.Constants; 031import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; 032import org.apache.commons.lang3.builder.ToStringBuilder; 033import org.apache.commons.lang3.builder.ToStringStyle; 034import org.hibernate.annotations.OptimisticLock; 035import org.hibernate.search.engine.backend.types.Projectable; 036import org.hibernate.search.engine.backend.types.Searchable; 037import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef; 038import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.RoutingBinderRef; 039import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; 040import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; 041import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; 042import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency; 043import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath; 044import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyBinding; 045import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue; 046import org.hl7.fhir.instance.model.api.IIdType; 047 048import javax.persistence.CascadeType; 049import javax.persistence.Column; 050import javax.persistence.Entity; 051import javax.persistence.FetchType; 052import javax.persistence.GeneratedValue; 053import javax.persistence.GenerationType; 054import javax.persistence.Id; 055import javax.persistence.Index; 056import javax.persistence.NamedEntityGraph; 057import javax.persistence.OneToMany; 058import javax.persistence.OneToOne; 059import javax.persistence.PrePersist; 060import javax.persistence.PreUpdate; 061import javax.persistence.SequenceGenerator; 062import javax.persistence.Table; 063import javax.persistence.Transient; 064import javax.persistence.Version; 065import java.io.Serializable; 066import java.util.ArrayList; 067import java.util.Collection; 068import java.util.HashSet; 069import java.util.Objects; 070import java.util.Set; 071import java.util.stream.Collectors; 072 073@Indexed(routingBinder= @RoutingBinderRef(type = ResourceTableRoutingBinder.class)) 074@Entity 075@Table(name = "HFJ_RESOURCE", uniqueConstraints = {}, indexes = { 076 @Index(name = "IDX_RES_DATE", columnList = "RES_UPDATED"), 077 @Index(name = "IDX_RES_TYPE", columnList = "RES_TYPE"), 078 @Index(name = "IDX_INDEXSTATUS", columnList = "SP_INDEX_STATUS") 079}) 080@NamedEntityGraph(name = "Resource.noJoins") 081public class ResourceTable extends BaseHasResource implements Serializable, IBasePersistedResource, IResourceLookup { 082 public static final int RESTYPE_LEN = 40; 083 private static final int MAX_LANGUAGE_LENGTH = 20; 084 private static final long serialVersionUID = 1L; 085 086 /** 087 * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB 088 * Note the extra config needed in HS6 for indexing transient props: 089 * https://docs.jboss.org/hibernate/search/6.0/migration/html_single/#indexed-transient-requires-configuration 090 * 091 * Note that we depend on `myVersion` updated for this field to be indexed. 092 */ 093 @Transient 094 @FullTextField(name = "myContentText", searchable = Searchable.YES, projectable = Projectable.YES, analyzer = "standardAnalyzer") 095 @FullTextField(name = "myContentTextEdgeNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer = "autocompleteEdgeAnalyzer") 096 @FullTextField(name = "myContentTextNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer = "autocompleteNGramAnalyzer") 097 @FullTextField(name = "myContentTextPhonetic", searchable= Searchable.YES, projectable= Projectable.NO, analyzer = "autocompletePhoneticAnalyzer") 098 @OptimisticLock(excluded = true) 099 @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion"))) 100 private String myContentText; 101 102 @Column(name = "HASH_SHA256", length = 64, nullable = true) 103 @OptimisticLock(excluded = true) 104 private String myHashSha256; 105 106 @Column(name = "SP_HAS_LINKS") 107 @OptimisticLock(excluded = true) 108 private boolean myHasLinks; 109 110 @Id 111 @SequenceGenerator(name = "SEQ_RESOURCE_ID", sequenceName = "SEQ_RESOURCE_ID") 112 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID") 113 @Column(name = "RES_ID") 114 @GenericField(projectable = Projectable.YES) 115 private Long myId; 116 117 @Column(name = "SP_INDEX_STATUS", nullable = true) 118 @OptimisticLock(excluded = true) 119 private Long myIndexStatus; 120 121 // TODO: Removed in 5.5.0. Drop in a future release. 122 @Column(name = "RES_LANGUAGE", length = MAX_LANGUAGE_LENGTH, nullable = true) 123 @OptimisticLock(excluded = true) 124 private String myLanguage; 125 126 /** 127 * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB 128 */ 129 @Transient() 130 @FullTextField(name = "myNarrativeText", searchable = Searchable.YES, projectable = Projectable.YES, analyzer = "standardAnalyzer") 131 @FullTextField(name = "myNarrativeTextEdgeNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer = "autocompleteEdgeAnalyzer") 132 @FullTextField(name = "myNarrativeTextNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer = "autocompleteNGramAnalyzer") 133 @FullTextField(name = "myNarrativeTextPhonetic", searchable= Searchable.YES, projectable= Projectable.NO, analyzer = "autocompletePhoneticAnalyzer") 134 @OptimisticLock(excluded = true) 135 @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion"))) 136 private String myNarrativeText; 137 138 @Transient 139 @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion"))) 140 @PropertyBinding(binder = @PropertyBinderRef(type = SearchParamTextPropertyBinder.class)) 141 private ExtendedLuceneIndexData myLuceneIndexData; 142 143 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 144 @OptimisticLock(excluded = true) 145 private Collection<ResourceIndexedSearchParamCoords> myParamsCoords; 146 147 @Column(name = "SP_COORDS_PRESENT") 148 @OptimisticLock(excluded = true) 149 private boolean myParamsCoordsPopulated; 150 151 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 152 @OptimisticLock(excluded = true) 153 private Collection<ResourceIndexedSearchParamDate> myParamsDate; 154 155 @Column(name = "SP_DATE_PRESENT") 156 @OptimisticLock(excluded = true) 157 private boolean myParamsDatePopulated; 158 159 @OptimisticLock(excluded = true) 160 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 161 private Collection<ResourceIndexedSearchParamNumber> myParamsNumber; 162 163 @Column(name = "SP_NUMBER_PRESENT") 164 @OptimisticLock(excluded = true) 165 private boolean myParamsNumberPopulated; 166 167 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 168 @OptimisticLock(excluded = true) 169 private Collection<ResourceIndexedSearchParamQuantity> myParamsQuantity; 170 171 @Column(name = "SP_QUANTITY_PRESENT") 172 @OptimisticLock(excluded = true) 173 private boolean myParamsQuantityPopulated; 174 175 /** 176 * Added to support UCUM conversion 177 * since 5.3.0 178 */ 179 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 180 @OptimisticLock(excluded = true) 181 private Collection<ResourceIndexedSearchParamQuantityNormalized> myParamsQuantityNormalized; 182 183 /** 184 * Added to support UCUM conversion, 185 * NOTE : use Boolean class instead of boolean primitive, in order to set the existing rows to null 186 * since 5.3.0 187 */ 188 @Column(name = "SP_QUANTITY_NRML_PRESENT") 189 @OptimisticLock(excluded = true) 190 private Boolean myParamsQuantityNormalizedPopulated = Boolean.FALSE; 191 192 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 193 @OptimisticLock(excluded = true) 194 private Collection<ResourceIndexedSearchParamString> myParamsString; 195 196 @Column(name = "SP_STRING_PRESENT") 197 @OptimisticLock(excluded = true) 198 private boolean myParamsStringPopulated; 199 200 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 201 @OptimisticLock(excluded = true) 202 private Collection<ResourceIndexedSearchParamToken> myParamsToken; 203 204 @Column(name = "SP_TOKEN_PRESENT") 205 @OptimisticLock(excluded = true) 206 private boolean myParamsTokenPopulated; 207 208 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 209 @OptimisticLock(excluded = true) 210 private Collection<ResourceIndexedSearchParamUri> myParamsUri; 211 212 @Column(name = "SP_URI_PRESENT") 213 @OptimisticLock(excluded = true) 214 private boolean myParamsUriPopulated; 215 216 // Added in 3.0.0 - Should make this a primitive Boolean at some point 217 @OptimisticLock(excluded = true) 218 @Column(name = "SP_CMPSTR_UNIQ_PRESENT") 219 private Boolean myParamsComboStringUniquePresent = false; 220 221 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 222 @OptimisticLock(excluded = true) 223 private Collection<ResourceIndexedComboStringUnique> myParamsComboStringUnique; 224 225 // Added in 5.5.0 - Should make this a primitive Boolean at some point 226 @OptimisticLock(excluded = true) 227 @Column(name = "SP_CMPTOKS_PRESENT") 228 private Boolean myParamsComboTokensNonUniquePresent = false; 229 230 @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 231 @OptimisticLock(excluded = true) 232 private Collection<ResourceIndexedComboTokenNonUnique> myParamsComboTokensNonUnique; 233 234 @OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 235 @OptimisticLock(excluded = true) 236 private Collection<ResourceLink> myResourceLinks; 237 238 /** 239 * This is a clone of {@link #myResourceLinks} but without the hibernate annotations. 240 * Before we persist we copy the contents of {@link #myResourceLinks} into this field. We 241 * have this separate because that way we can only populate this field if 242 * {@link #myHasLinks} is true, meaning that there are actually resource links present 243 * right now. This avoids Hibernate Search triggering a select on the resource link 244 * table. 245 * <p> 246 * This field is used by FulltextSearchSvcImpl 247 * <p> 248 * You can test that any changes don't cause extra queries by running 249 * FhirResourceDaoR4QueryCountTest 250 */ 251 @FullTextField 252 @Transient 253 @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myResourceLinks"))) 254 private String myResourceLinksField; 255 256 @OneToMany(mappedBy = "myTargetResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) 257 @OptimisticLock(excluded = true) 258 private Collection<ResourceLink> myResourceLinksAsTarget; 259 260 @Column(name = "RES_TYPE", length = RESTYPE_LEN, nullable = false) 261 @FullTextField 262 @OptimisticLock(excluded = true) 263 private String myResourceType; 264 265 @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) 266 @OptimisticLock(excluded = true) 267 private Collection<SearchParamPresent> mySearchParamPresents; 268 269 @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) 270 @OptimisticLock(excluded = true) 271 private Set<ResourceTag> myTags; 272 273 @Transient 274 private transient boolean myUnchangedInCurrentOperation; 275 276 @Version 277 @Column(name = "RES_VER") 278 private long myVersion; 279 280 @OneToMany(mappedBy = "myResourceTable", fetch = FetchType.LAZY) 281 private Collection<ResourceHistoryProvenanceEntity> myProvenance; 282 283 @Transient 284 private transient ResourceHistoryTable myCurrentVersionEntity; 285 286 @OneToOne(optional = true, fetch = FetchType.EAGER, cascade = {}, orphanRemoval = false, mappedBy = "myResource") 287 @OptimisticLock(excluded = true) 288 private ForcedId myForcedId; 289 290 @Transient 291 private volatile String myCreatedByMatchUrl; 292 293 /** 294 * Constructor 295 */ 296 public ResourceTable() { 297 super(); 298 } 299 300 @Override 301 public ResourceTag addTag(TagDefinition theTag) { 302 for (ResourceTag next : getTags()) { 303 if (next.getTag().equals(theTag)) { 304 return next; 305 } 306 } 307 ResourceTag tag = new ResourceTag(this, theTag, getPartitionId()); 308 getTags().add(tag); 309 return tag; 310 } 311 312 313 public String getHashSha256() { 314 return myHashSha256; 315 } 316 317 public void setHashSha256(String theHashSha256) { 318 myHashSha256 = theHashSha256; 319 } 320 321 @Override 322 public Long getId() { 323 return myId; 324 } 325 326 public void setId(Long theId) { 327 myId = theId; 328 } 329 330 public Long getIndexStatus() { 331 return myIndexStatus; 332 } 333 334 public void setIndexStatus(Long theIndexStatus) { 335 myIndexStatus = theIndexStatus; 336 } 337 338 public Collection<ResourceIndexedComboStringUnique> getParamsComboStringUnique() { 339 if (myParamsComboStringUnique == null) { 340 myParamsComboStringUnique = new ArrayList<>(); 341 } 342 return myParamsComboStringUnique; 343 } 344 345 public Collection<ResourceIndexedComboTokenNonUnique> getmyParamsComboTokensNonUnique() { 346 if (myParamsComboTokensNonUnique == null) { 347 myParamsComboTokensNonUnique = new ArrayList<>(); 348 } 349 return myParamsComboTokensNonUnique; 350 } 351 352 public Collection<ResourceIndexedSearchParamCoords> getParamsCoords() { 353 if (myParamsCoords == null) { 354 myParamsCoords = new ArrayList<>(); 355 } 356 return myParamsCoords; 357 } 358 359 public void setParamsCoords(Collection<ResourceIndexedSearchParamCoords> theParamsCoords) { 360 if (!isParamsTokenPopulated() && theParamsCoords.isEmpty()) { 361 return; 362 } 363 getParamsCoords().clear(); 364 getParamsCoords().addAll(theParamsCoords); 365 } 366 367 public Collection<ResourceIndexedSearchParamDate> getParamsDate() { 368 if (myParamsDate == null) { 369 myParamsDate = new ArrayList<>(); 370 } 371 return myParamsDate; 372 } 373 374 public void setParamsDate(Collection<ResourceIndexedSearchParamDate> theParamsDate) { 375 if (!isParamsDatePopulated() && theParamsDate.isEmpty()) { 376 return; 377 } 378 getParamsDate().clear(); 379 getParamsDate().addAll(theParamsDate); 380 } 381 382 public Collection<ResourceIndexedSearchParamNumber> getParamsNumber() { 383 if (myParamsNumber == null) { 384 myParamsNumber = new ArrayList<>(); 385 } 386 return myParamsNumber; 387 } 388 389 public void setParamsNumber(Collection<ResourceIndexedSearchParamNumber> theNumberParams) { 390 if (!isParamsNumberPopulated() && theNumberParams.isEmpty()) { 391 return; 392 } 393 getParamsNumber().clear(); 394 getParamsNumber().addAll(theNumberParams); 395 } 396 397 public Collection<ResourceIndexedSearchParamQuantity> getParamsQuantity() { 398 if (myParamsQuantity == null) { 399 myParamsQuantity = new ArrayList<>(); 400 } 401 return myParamsQuantity; 402 } 403 404 public void setParamsQuantity(Collection<ResourceIndexedSearchParamQuantity> theQuantityParams) { 405 if (!isParamsQuantityPopulated() && theQuantityParams.isEmpty()) { 406 return; 407 } 408 getParamsQuantity().clear(); 409 getParamsQuantity().addAll(theQuantityParams); 410 } 411 412 public Collection<ResourceIndexedSearchParamQuantityNormalized> getParamsQuantityNormalized() { 413 if (myParamsQuantityNormalized == null) { 414 myParamsQuantityNormalized = new ArrayList<>(); 415 } 416 return myParamsQuantityNormalized; 417 } 418 419 public void setParamsQuantityNormalized(Collection<ResourceIndexedSearchParamQuantityNormalized> theQuantityNormalizedParams) { 420 if (!isParamsQuantityNormalizedPopulated() && theQuantityNormalizedParams.isEmpty()) { 421 return; 422 } 423 getParamsQuantityNormalized().clear(); 424 getParamsQuantityNormalized().addAll(theQuantityNormalizedParams); 425 } 426 427 public Collection<ResourceIndexedSearchParamString> getParamsString() { 428 if (myParamsString == null) { 429 myParamsString = new ArrayList<>(); 430 } 431 return myParamsString; 432 } 433 434 public void setParamsString(Collection<ResourceIndexedSearchParamString> theParamsString) { 435 if (!isParamsStringPopulated() && theParamsString.isEmpty()) { 436 return; 437 } 438 getParamsString().clear(); 439 getParamsString().addAll(theParamsString); 440 } 441 442 public Collection<ResourceIndexedSearchParamToken> getParamsToken() { 443 if (myParamsToken == null) { 444 myParamsToken = new ArrayList<>(); 445 } 446 return myParamsToken; 447 } 448 449 public void setParamsToken(Collection<ResourceIndexedSearchParamToken> theParamsToken) { 450 if (!isParamsTokenPopulated() && theParamsToken.isEmpty()) { 451 return; 452 } 453 getParamsToken().clear(); 454 getParamsToken().addAll(theParamsToken); 455 } 456 457 public Collection<ResourceIndexedSearchParamUri> getParamsUri() { 458 if (myParamsUri == null) { 459 myParamsUri = new ArrayList<>(); 460 } 461 return myParamsUri; 462 } 463 464 public void setParamsUri(Collection<ResourceIndexedSearchParamUri> theParamsUri) { 465 if (!isParamsTokenPopulated() && theParamsUri.isEmpty()) { 466 return; 467 } 468 getParamsUri().clear(); 469 getParamsUri().addAll(theParamsUri); 470 } 471 472 @Override 473 public Long getResourceId() { 474 return getId(); 475 } 476 477 public Collection<ResourceLink> getResourceLinks() { 478 if (myResourceLinks == null) { 479 myResourceLinks = new ArrayList<>(); 480 } 481 return myResourceLinks; 482 } 483 484 public void setResourceLinks(Collection<ResourceLink> theLinks) { 485 if (!isHasLinks() && theLinks.isEmpty()) { 486 return; 487 } 488 getResourceLinks().clear(); 489 getResourceLinks().addAll(theLinks); 490 } 491 492 @Override 493 public String getResourceType() { 494 return myResourceType; 495 } 496 497 public ResourceTable setResourceType(String theResourceType) { 498 myResourceType = theResourceType; 499 return this; 500 } 501 502 @Override 503 public Collection<ResourceTag> getTags() { 504 if (myTags == null) { 505 myTags = new HashSet<>(); 506 } 507 return myTags; 508 } 509 510 @Override 511 public long getVersion() { 512 return myVersion; 513 } 514 515 public void setVersion(long theVersion) { 516 myVersion = theVersion; 517 } 518 519 public boolean isHasLinks() { 520 return myHasLinks; 521 } 522 523 public void setHasLinks(boolean theHasLinks) { 524 myHasLinks = theHasLinks; 525 } 526 527 public boolean isParamsComboStringUniquePresent() { 528 if (myParamsComboStringUniquePresent == null) { 529 return false; 530 } 531 return myParamsComboStringUniquePresent; 532 } 533 534 public void setParamsComboStringUniquePresent(boolean theParamsComboStringUniquePresent) { 535 myParamsComboStringUniquePresent = theParamsComboStringUniquePresent; 536 } 537 538 public boolean isParamsComboTokensNonUniquePresent() { 539 if (myParamsComboTokensNonUniquePresent == null) { 540 return false; 541 } 542 return myParamsComboTokensNonUniquePresent; 543 } 544 545 public void setParamsComboTokensNonUniquePresent(boolean theParamsComboTokensNonUniquePresent) { 546 myParamsComboStringUniquePresent = theParamsComboTokensNonUniquePresent; 547 } 548 549 public boolean isParamsCoordsPopulated() { 550 return myParamsCoordsPopulated; 551 } 552 553 public void setParamsCoordsPopulated(boolean theParamsCoordsPopulated) { 554 myParamsCoordsPopulated = theParamsCoordsPopulated; 555 } 556 557 public boolean isParamsDatePopulated() { 558 return myParamsDatePopulated; 559 } 560 561 public void setParamsDatePopulated(boolean theParamsDatePopulated) { 562 myParamsDatePopulated = theParamsDatePopulated; 563 } 564 565 public boolean isParamsNumberPopulated() { 566 return myParamsNumberPopulated; 567 } 568 569 public void setParamsNumberPopulated(boolean theParamsNumberPopulated) { 570 myParamsNumberPopulated = theParamsNumberPopulated; 571 } 572 573 public boolean isParamsQuantityPopulated() { 574 return myParamsQuantityPopulated; 575 } 576 577 public void setParamsQuantityPopulated(boolean theParamsQuantityPopulated) { 578 myParamsQuantityPopulated = theParamsQuantityPopulated; 579 } 580 581 public Boolean isParamsQuantityNormalizedPopulated() { 582 if (myParamsQuantityNormalizedPopulated == null) 583 return Boolean.FALSE; 584 else 585 return myParamsQuantityNormalizedPopulated; 586 } 587 588 public void setParamsQuantityNormalizedPopulated(Boolean theParamsQuantityNormalizedPopulated) { 589 if (theParamsQuantityNormalizedPopulated == null) 590 myParamsQuantityNormalizedPopulated = Boolean.FALSE; 591 else 592 myParamsQuantityNormalizedPopulated = theParamsQuantityNormalizedPopulated; 593 } 594 595 public boolean isParamsStringPopulated() { 596 return myParamsStringPopulated; 597 } 598 599 public void setParamsStringPopulated(boolean theParamsStringPopulated) { 600 myParamsStringPopulated = theParamsStringPopulated; 601 } 602 603 public boolean isParamsTokenPopulated() { 604 return myParamsTokenPopulated; 605 } 606 607 public void setParamsTokenPopulated(boolean theParamsTokenPopulated) { 608 myParamsTokenPopulated = theParamsTokenPopulated; 609 } 610 611 public boolean isParamsUriPopulated() { 612 return myParamsUriPopulated; 613 } 614 615 public void setParamsUriPopulated(boolean theParamsUriPopulated) { 616 myParamsUriPopulated = theParamsUriPopulated; 617 } 618 619 /** 620 * Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation 621 * and was not re-saved in the database 622 */ 623 public boolean isUnchangedInCurrentOperation() { 624 return myUnchangedInCurrentOperation; 625 } 626 627 /** 628 * Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation 629 * and was not re-saved in the database 630 */ 631 public void setUnchangedInCurrentOperation(boolean theUnchangedInCurrentOperation) { 632 633 myUnchangedInCurrentOperation = theUnchangedInCurrentOperation; 634 } 635 636 public void setContentText(String theContentText) { 637 myContentText = theContentText; 638 } 639 640 public String getContentText() { 641 return myContentText; 642 } 643 644 public void setNarrativeText(String theNarrativeText) { 645 myNarrativeText = theNarrativeText; 646 } 647 648 public ResourceHistoryTable toHistory(boolean theCreateVersionTags) { 649 ResourceHistoryTable retVal = new ResourceHistoryTable(); 650 651 retVal.setResourceId(myId); 652 retVal.setResourceType(myResourceType); 653 retVal.setVersion(myVersion); 654 retVal.setTransientForcedId(getTransientForcedId()); 655 656 retVal.setPublished(getPublishedDate()); 657 retVal.setUpdated(getUpdatedDate()); 658 retVal.setFhirVersion(getFhirVersion()); 659 retVal.setDeleted(getDeleted()); 660 retVal.setResourceTable(this); 661 retVal.setForcedId(getForcedId()); 662 retVal.setPartitionId(getPartitionId()); 663 664 retVal.getTags().clear(); 665 666 retVal.setHasTags(isHasTags()); 667 if (isHasTags() && theCreateVersionTags) { 668 for (ResourceTag next : getTags()) { 669 retVal.addTag(next); 670 } 671 } 672 673 return retVal; 674 } 675 676 @Override 677 public String toString() { 678 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 679 b.append("pid", myId); 680 b.append("resourceType", myResourceType); 681 b.append("version", myVersion); 682 if (getPartitionId() != null) { 683 b.append("partitionId", getPartitionId().getPartitionId()); 684 } 685 b.append("lastUpdated", getUpdated().getValueAsString()); 686 if (getDeleted() != null) { 687 b.append("deleted"); 688 } 689 return b.build(); 690 } 691 692 @PrePersist 693 @PreUpdate 694 public void preSave() { 695 if (myHasLinks && myResourceLinks != null) { 696 myResourceLinksField = getResourceLinks() 697 .stream() 698 .map(ResourceLink::getTargetResourcePid) 699 .filter(Objects::nonNull) 700 .map(Object::toString) 701 .collect(Collectors.joining(" ")); 702 } else { 703 myResourceLinksField = null; 704 } 705 } 706 707 /** 708 * This is a convenience to avoid loading the version a second time within a single transaction. It is 709 * not persisted. 710 */ 711 public void setCurrentVersionEntity(ResourceHistoryTable theCurrentVersionEntity) { 712 myCurrentVersionEntity = theCurrentVersionEntity; 713 } 714 715 /** 716 * This is a convenience to avoid loading the version a second time within a single transaction. It is 717 * not persisted. 718 */ 719 public ResourceHistoryTable getCurrentVersionEntity() { 720 return myCurrentVersionEntity; 721 } 722 723 @Override 724 public ResourcePersistentId getPersistentId() { 725 return new ResourcePersistentId(getId()); 726 } 727 728 @Override 729 public ForcedId getForcedId() { 730 return myForcedId; 731 } 732 733 @Override 734 public void setForcedId(ForcedId theForcedId) { 735 myForcedId = theForcedId; 736 } 737 738 739 740 @Override 741 public IdDt getIdDt() { 742 IdDt retVal = new IdDt(); 743 populateId(retVal); 744 return retVal; 745 } 746 747 748 public IIdType getIdType(FhirContext theContext) { 749 IIdType retVal = theContext.getVersion().newIdType(); 750 populateId(retVal); 751 return retVal; 752 } 753 754 private void populateId(IIdType retVal) { 755 if (getTransientForcedId() != null) { 756 // Avoid a join query if possible 757 retVal.setValue(getResourceType() + '/' + getTransientForcedId() + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); 758 } else if (getForcedId() == null) { 759 Long id = this.getResourceId(); 760 retVal.setValue(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); 761 } else { 762 String forcedId = getForcedId().getForcedId(); 763 retVal.setValue(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); 764 } 765 } 766 767 public void setCreatedByMatchUrl(String theCreatedByMatchUrl) { 768 myCreatedByMatchUrl = theCreatedByMatchUrl; 769 } 770 771 public String getCreatedByMatchUrl() { 772 return myCreatedByMatchUrl; 773 } 774 775 public void setLuceneIndexData(ExtendedLuceneIndexData theLuceneIndexData) { 776 myLuceneIndexData = theLuceneIndexData; 777 } 778}