001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 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.entity; 021 022import ca.uhn.fhir.jpa.model.dao.JpaPid; 023import ca.uhn.fhir.jpa.model.entity.AuditableBasePartitionable; 024import ca.uhn.fhir.jpa.model.entity.ResourceTable; 025import ca.uhn.fhir.mdm.api.IMdmLink; 026import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; 027import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; 028import com.fasterxml.jackson.annotation.JsonIgnore; 029import jakarta.persistence.Column; 030import jakarta.persistence.Entity; 031import jakarta.persistence.EnumType; 032import jakarta.persistence.Enumerated; 033import jakarta.persistence.FetchType; 034import jakarta.persistence.ForeignKey; 035import jakarta.persistence.GeneratedValue; 036import jakarta.persistence.GenerationType; 037import jakarta.persistence.Id; 038import jakarta.persistence.Index; 039import jakarta.persistence.JoinColumn; 040import jakarta.persistence.ManyToOne; 041import jakarta.persistence.SequenceGenerator; 042import jakarta.persistence.Table; 043import jakarta.persistence.Temporal; 044import jakarta.persistence.TemporalType; 045import jakarta.persistence.UniqueConstraint; 046import org.apache.commons.lang3.builder.ToStringBuilder; 047import org.hibernate.annotations.JdbcTypeCode; 048import org.hibernate.envers.AuditTable; 049import org.hibernate.envers.Audited; 050import org.hibernate.envers.NotAudited; 051import org.hibernate.type.SqlTypes; 052 053import java.util.Date; 054 055@Entity 056@Table( 057 name = "MPI_LINK", 058 uniqueConstraints = { 059 // TODO GGG DROP this index, and instead use the below one 060 @UniqueConstraint( 061 name = "IDX_EMPI_PERSON_TGT", 062 columnNames = {"PERSON_PID", "TARGET_PID"}), 063 // TODO GGG Should i make individual indices for PERSON/TARGET? 064 }, 065 indexes = { 066 @Index(name = "IDX_EMPI_MATCH_TGT_VER", columnList = "MATCH_RESULT, TARGET_PID, VERSION"), 067 // v---- this one 068 @Index(name = "IDX_EMPI_GR_TGT", columnList = "GOLDEN_RESOURCE_PID, TARGET_PID"), 069 @Index(name = "FK_EMPI_LINK_TARGET", columnList = "TARGET_PID"), 070 // indexes for metrics 071 @Index(name = "IDX_EMPI_TGT_MR_LS", columnList = "TARGET_TYPE, MATCH_RESULT, LINK_SOURCE"), 072 @Index(name = "IDX_EMPI_TGT_MR_SCORE", columnList = "TARGET_TYPE, MATCH_RESULT, SCORE") 073 }) 074@Audited 075// This is the table name generated by default by envers, but we set it explicitly for clarity 076@AuditTable("MPI_LINK_AUD") 077public class MdmLink extends AuditableBasePartitionable implements IMdmLink<JpaPid> { 078 public static final int VERSION_LENGTH = 16; 079 private static final int MATCH_RESULT_LENGTH = 16; 080 private static final int LINK_SOURCE_LENGTH = 16; 081 public static final int SOURCE_TYPE_LENGTH = 40; 082 083 @SequenceGenerator(name = "SEQ_EMPI_LINK_ID", sequenceName = "SEQ_EMPI_LINK_ID") 084 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EMPI_LINK_ID") 085 @Id 086 @Column(name = "PID") 087 private Long myId; 088 089 @ManyToOne( 090 optional = false, 091 fetch = FetchType.LAZY, 092 cascade = {}) 093 @JoinColumn( 094 name = "GOLDEN_RESOURCE_PID", 095 referencedColumnName = "RES_ID", 096 foreignKey = @ForeignKey(name = "FK_EMPI_LINK_GOLDEN_RESOURCE"), 097 insertable = false, 098 updatable = false, 099 nullable = false) 100 @NotAudited 101 private ResourceTable myGoldenResource; 102 103 @Column(name = "GOLDEN_RESOURCE_PID", nullable = false) 104 private Long myGoldenResourcePid; 105 106 @Deprecated 107 @ManyToOne( 108 optional = false, 109 fetch = FetchType.LAZY, 110 cascade = {}) 111 @JoinColumn( 112 name = "PERSON_PID", 113 referencedColumnName = "RES_ID", 114 foreignKey = @ForeignKey(name = "FK_EMPI_LINK_PERSON"), 115 insertable = false, 116 updatable = false, 117 nullable = false) 118 @NotAudited 119 private ResourceTable myPerson; 120 121 @Deprecated 122 @Column(name = "PERSON_PID", nullable = false) 123 private Long myPersonPid; 124 125 @ManyToOne( 126 optional = false, 127 fetch = FetchType.LAZY, 128 cascade = {}) 129 @JoinColumn( 130 name = "TARGET_PID", 131 referencedColumnName = "RES_ID", 132 foreignKey = @ForeignKey(name = "FK_EMPI_LINK_TARGET"), 133 insertable = false, 134 updatable = false, 135 nullable = false) 136 @NotAudited 137 private ResourceTable mySource; 138 139 @Column(name = "TARGET_PID", updatable = false, nullable = false) 140 private Long mySourcePid; 141 142 @Column(name = "MATCH_RESULT", nullable = false) 143 @Enumerated(EnumType.ORDINAL) 144 @JdbcTypeCode(SqlTypes.INTEGER) 145 private MdmMatchResultEnum myMatchResult; 146 147 @Column(name = "LINK_SOURCE", nullable = false) 148 @Enumerated(EnumType.ORDINAL) 149 @JdbcTypeCode(SqlTypes.INTEGER) 150 private MdmLinkSourceEnum myLinkSource; 151 152 @Temporal(TemporalType.TIMESTAMP) 153 @Column(name = "CREATED", nullable = false) 154 private Date myCreated; 155 156 @Temporal(TemporalType.TIMESTAMP) 157 @Column(name = "UPDATED", nullable = false) 158 private Date myUpdated; 159 160 @Column(name = "VERSION", nullable = false, length = VERSION_LENGTH) 161 private String myVersion; 162 163 /** This link was created as a result of an eid match **/ 164 @Column(name = "EID_MATCH") 165 private Boolean myEidMatch; 166 167 /** This link created a new person **/ 168 @Column(name = "NEW_PERSON") 169 private Boolean myHadToCreateNewGoldenResource; 170 171 @Column(name = "VECTOR") 172 @JsonIgnore 173 private Long myVector; 174 175 @Column(name = "SCORE") 176 private Double myScore; 177 178 // TODO GGG GL-1340 179 @Column(name = "RULE_COUNT") 180 private Long myRuleCount; 181 182 public MdmLink() {} 183 184 public MdmLink(String theVersion) { 185 myVersion = theVersion; 186 } 187 188 @Column(name = "TARGET_TYPE", nullable = true, length = SOURCE_TYPE_LENGTH) 189 private String myMdmSourceType; 190 191 @Override 192 public JpaPid getId() { 193 return JpaPid.fromId(myId); 194 } 195 196 @Override 197 public MdmLink setId(JpaPid theId) { 198 myId = theId.getId(); 199 return this; 200 } 201 202 @Override 203 public JpaPid getGoldenResourcePersistenceId() { 204 return JpaPid.fromId(myGoldenResourcePid); 205 } 206 207 @Override 208 public IMdmLink setGoldenResourcePersistenceId(JpaPid theGoldenResourcePid) { 209 Long longPid = theGoldenResourcePid.getId(); 210 setPersonPid(longPid); 211 212 myGoldenResourcePid = longPid; 213 return this; 214 } 215 216 @Override 217 public JpaPid getSourcePersistenceId() { 218 return JpaPid.fromId(mySourcePid); 219 } 220 221 @Override 222 public IMdmLink setSourcePersistenceId(JpaPid theSourcePid) { 223 mySourcePid = theSourcePid.getId(); 224 return this; 225 } 226 227 public ResourceTable getGoldenResource() { 228 return myGoldenResource; 229 } 230 231 public MdmLink setGoldenResource(ResourceTable theGoldenResource) { 232 myGoldenResource = theGoldenResource; 233 myGoldenResourcePid = theGoldenResource.getId(); 234 235 myPerson = theGoldenResource; 236 myPersonPid = theGoldenResource.getId(); 237 238 return this; 239 } 240 241 @Deprecated 242 public Long getGoldenResourcePid() { 243 return myGoldenResourcePid; 244 } 245 246 /** 247 * @deprecated Use {@link #setGoldenResourcePid(Long)} instead 248 */ 249 @Deprecated 250 public MdmLink setPersonPid(Long thePersonPid) { 251 myPersonPid = thePersonPid; 252 return this; 253 } 254 255 /** 256 * @deprecated Use {@link #setGoldenResourcePersistenceId(JpaPid)} instead 257 */ 258 @Deprecated 259 public MdmLink setGoldenResourcePid(Long theGoldenResourcePid) { 260 setPersonPid(theGoldenResourcePid); 261 262 myGoldenResourcePid = theGoldenResourcePid; 263 return this; 264 } 265 266 public ResourceTable getSource() { 267 return mySource; 268 } 269 270 public MdmLink setSource(ResourceTable theSource) { 271 mySource = theSource; 272 mySourcePid = theSource.getId(); 273 return this; 274 } 275 276 @Deprecated 277 public Long getSourcePid() { 278 return mySourcePid; 279 } 280 281 /** 282 * @deprecated Use {@link #setSourcePersistenceId(JpaPid)} instead 283 */ 284 @Deprecated 285 public MdmLink setSourcePid(Long theSourcePid) { 286 mySourcePid = theSourcePid; 287 return this; 288 } 289 290 @Override 291 public MdmMatchResultEnum getMatchResult() { 292 return myMatchResult; 293 } 294 295 @Override 296 public MdmLink setMatchResult(MdmMatchResultEnum theMatchResult) { 297 myMatchResult = theMatchResult; 298 return this; 299 } 300 301 @Override 302 public MdmLinkSourceEnum getLinkSource() { 303 return myLinkSource; 304 } 305 306 @Override 307 public MdmLink setLinkSource(MdmLinkSourceEnum theLinkSource) { 308 myLinkSource = theLinkSource; 309 return this; 310 } 311 312 @Override 313 public Date getCreated() { 314 return myCreated; 315 } 316 317 @Override 318 public MdmLink setCreated(Date theCreated) { 319 myCreated = theCreated; 320 return this; 321 } 322 323 @Override 324 public Date getUpdated() { 325 return myUpdated; 326 } 327 328 @Override 329 public MdmLink setUpdated(Date theUpdated) { 330 myUpdated = theUpdated; 331 return this; 332 } 333 334 @Override 335 public String getVersion() { 336 return myVersion; 337 } 338 339 @Override 340 public MdmLink setVersion(String theVersion) { 341 myVersion = theVersion; 342 return this; 343 } 344 345 @Override 346 public Long getVector() { 347 return myVector; 348 } 349 350 @Override 351 public MdmLink setVector(Long theVector) { 352 myVector = theVector; 353 return this; 354 } 355 356 @Override 357 public Double getScore() { 358 return myScore; 359 } 360 361 @Override 362 public MdmLink setScore(Double theScore) { 363 myScore = theScore; 364 return this; 365 } 366 367 public Boolean getEidMatch() { 368 return myEidMatch; 369 } 370 371 /** 372 * Note that this method can not be called <code>getEidMatch</code> or 373 * <code>isEidMatch</code> because Hibernate Search complains about having 374 * 2 accessors for this property 375 */ 376 @Override 377 public Boolean isEidMatchPresent() { 378 return myEidMatch != null && myEidMatch; 379 } 380 381 @Override 382 public MdmLink setEidMatch(Boolean theEidMatch) { 383 myEidMatch = theEidMatch; 384 return this; 385 } 386 387 @Override 388 public Boolean getHadToCreateNewGoldenResource() { 389 return myHadToCreateNewGoldenResource != null && myHadToCreateNewGoldenResource; 390 } 391 392 @Override 393 public MdmLink setHadToCreateNewGoldenResource(Boolean theHadToCreateNewResource) { 394 myHadToCreateNewGoldenResource = theHadToCreateNewResource; 395 return this; 396 } 397 398 public MdmLink setMdmSourceType(String mdmSourceType) { 399 myMdmSourceType = mdmSourceType; 400 return this; 401 } 402 403 @Override 404 public String toString() { 405 return new ToStringBuilder(this) 406 .append("myId", myId) 407 .append("myGoldenResource", myGoldenResourcePid) 408 .append("mySourcePid", mySourcePid) 409 .append("myMdmSourceType", myMdmSourceType) 410 .append("myMatchResult", myMatchResult) 411 .append("myLinkSource", myLinkSource) 412 .append("myEidMatch", myEidMatch) 413 .append("myHadToCreateNewResource", myHadToCreateNewGoldenResource) 414 .append("myScore", myScore) 415 .append("myRuleCount", myRuleCount) 416 .append("myPartitionId", getPartitionId()) 417 .toString(); 418 } 419 420 public String getMdmSourceType() { 421 return myMdmSourceType; 422 } 423 424 public Long getRuleCount() { 425 return myRuleCount; 426 } 427 428 public MdmLink setRuleCount(Long theRuleCount) { 429 myRuleCount = theRuleCount; 430 return this; 431 } 432}