001/*
002 * #%L
003 * HAPI FHIR JPA Server
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.entity;
021
022import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
023import ca.uhn.hapi.fhir.sql.hibernatesvc.PartitionedIdProperty;
024import jakarta.persistence.Column;
025import jakarta.persistence.Embeddable;
026import jakarta.persistence.EmbeddedId;
027import jakarta.persistence.Entity;
028import jakarta.persistence.EnumType;
029import jakarta.persistence.Enumerated;
030import jakarta.persistence.FetchType;
031import jakarta.persistence.ForeignKey;
032import jakarta.persistence.GeneratedValue;
033import jakarta.persistence.GenerationType;
034import jakarta.persistence.Index;
035import jakarta.persistence.JoinColumn;
036import jakarta.persistence.JoinColumns;
037import jakarta.persistence.ManyToOne;
038import jakarta.persistence.PrePersist;
039import jakarta.persistence.SequenceGenerator;
040import jakarta.persistence.Table;
041import org.apache.commons.lang3.builder.ToStringBuilder;
042import org.apache.commons.lang3.builder.ToStringStyle;
043import org.hibernate.annotations.JdbcTypeCode;
044import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
045import org.hibernate.type.SqlTypes;
046
047import java.io.Serializable;
048import java.util.Objects;
049
050@Entity
051@Table(
052                name = "TRM_CONCEPT_PC_LINK",
053                indexes = {
054                        // must have same name that indexed FK or SchemaMigrationTest complains because H2 sets this index
055                        // automatically
056                        @Index(name = "FK_TERM_CONCEPTPC_CHILD", columnList = "CHILD_PID", unique = false),
057                        @Index(name = "FK_TERM_CONCEPTPC_PARENT", columnList = "PARENT_PID", unique = false),
058                        @Index(name = "FK_TERM_CONCEPTPC_CS", columnList = "CODESYSTEM_PID")
059                })
060public class TermConceptParentChildLink implements Serializable {
061        private static final long serialVersionUID = 1L;
062
063        @ManyToOne(fetch = FetchType.LAZY)
064        @JoinColumns(
065                        value = {
066                                @JoinColumn(
067                                                name = "CHILD_PID",
068                                                insertable = false,
069                                                updatable = false,
070                                                nullable = false,
071                                                referencedColumnName = "PID"),
072                                @JoinColumn(
073                                                name = "PARTITION_ID",
074                                                referencedColumnName = "PARTITION_ID",
075                                                insertable = false,
076                                                updatable = false,
077                                                nullable = false)
078                        },
079                        foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CHILD"))
080        private TermConcept myChild;
081
082        @Column(name = "CHILD_PID", insertable = true, updatable = true, nullable = false)
083        private Long myChildPid;
084
085        @ManyToOne(fetch = FetchType.LAZY)
086        @JoinColumns(
087                        value = {
088                                @JoinColumn(
089                                                name = "CODESYSTEM_PID",
090                                                referencedColumnName = "PID",
091                                                insertable = false,
092                                                updatable = false,
093                                                nullable = false),
094                                @JoinColumn(
095                                                name = "PARTITION_ID",
096                                                referencedColumnName = "PARTITION_ID",
097                                                insertable = false,
098                                                updatable = false,
099                                                nullable = false)
100                        },
101                        foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CS"))
102        private TermCodeSystemVersion myCodeSystem;
103
104        @Column(name = "CODESYSTEM_PID", insertable = true, updatable = true, nullable = false)
105        @FullTextField(name = "myCodeSystemVersionPid")
106        private Long myCodeSystemVersionPid;
107
108        @ManyToOne(
109                        fetch = FetchType.LAZY,
110                        cascade = {})
111        @JoinColumns(
112                        value = {
113                                @JoinColumn(
114                                                name = "PARENT_PID",
115                                                insertable = false,
116                                                updatable = false,
117                                                nullable = false,
118                                                referencedColumnName = "PID"),
119                                @JoinColumn(
120                                                name = "PARTITION_ID",
121                                                referencedColumnName = "PARTITION_ID",
122                                                insertable = false,
123                                                updatable = false,
124                                                nullable = false)
125                        },
126                        foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT"))
127        private TermConcept myParent;
128
129        @Column(name = "PARENT_PID", insertable = true, updatable = true, nullable = false)
130        private Long myParentPid;
131
132        @EmbeddedId
133        private TermConceptParentChildLinkPk myId;
134
135        @Column(name = PartitionablePartitionId.PARTITION_ID, nullable = true, insertable = false, updatable = false)
136        private Integer myPartitionIdValue;
137
138        @Enumerated(EnumType.ORDINAL)
139        @Column(name = "REL_TYPE", length = 5, nullable = true)
140        @JdbcTypeCode(SqlTypes.INTEGER)
141        private RelationshipTypeEnum myRelationshipType;
142
143        @Override
144        public boolean equals(Object obj) {
145                if (this == obj) return true;
146                if (obj == null) return false;
147                if (getClass() != obj.getClass()) return false;
148                TermConceptParentChildLink other = (TermConceptParentChildLink) obj;
149                if (myChild == null) {
150                        if (other.myChild != null) return false;
151                } else if (!myChild.equals(other.myChild)) return false;
152                if (myCodeSystem == null) {
153                        if (other.myCodeSystem != null) return false;
154                } else if (!myCodeSystem.equals(other.myCodeSystem)) return false;
155                if (myParent == null) {
156                        if (other.myParent != null) return false;
157                } else if (!myParent.equals(other.myParent)) return false;
158                if (myRelationshipType != other.myRelationshipType) return false;
159                return true;
160        }
161
162        public TermConcept getChild() {
163                return myChild;
164        }
165
166        public Long getChildPid() {
167                return myChildPid;
168        }
169
170        public TermCodeSystemVersion getCodeSystem() {
171                return myCodeSystem;
172        }
173
174        public TermConceptParentChildLinkPk getPid() {
175                if (myId == null) {
176                        myId = new TermConceptParentChildLinkPk();
177                }
178                return myId;
179        }
180
181        public Long getId() {
182                return getPid().myId;
183        }
184
185        public TermConcept getParent() {
186                return myParent;
187        }
188
189        public Long getParentPid() {
190                return myParentPid;
191        }
192
193        public RelationshipTypeEnum getRelationshipType() {
194                return myRelationshipType;
195        }
196
197        @PrePersist
198        public void prePersist() {
199                if (myChildPid == null) {
200                        myChildPid = myChild.getId();
201                        assert myChildPid != null;
202                }
203                if (myParentPid == null) {
204                        myParentPid = myParent.getId();
205                        assert myParentPid != null;
206                }
207                if (myCodeSystemVersionPid == null) {
208                        myCodeSystemVersionPid = myCodeSystem.getPid();
209                        assert myCodeSystemVersionPid != null;
210                }
211        }
212
213        @Override
214        public int hashCode() {
215                final int prime = 31;
216                int result = 1;
217                result = prime * result + ((myChild == null) ? 0 : myChild.hashCode());
218                result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode());
219                result = prime * result + ((myParent == null) ? 0 : myParent.hashCode());
220                result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode());
221                return result;
222        }
223
224        public TermConceptParentChildLink setChild(TermConcept theChild) {
225                myChild = theChild;
226                myChildPid = theChild.getId();
227                return this;
228        }
229
230        public TermConceptParentChildLink setCodeSystem(TermCodeSystemVersion theCodeSystemVersion) {
231                myCodeSystem = theCodeSystemVersion;
232                myCodeSystemVersionPid = theCodeSystemVersion.getPid();
233                return this;
234        }
235
236        public TermConceptParentChildLink setParent(TermConcept theParent) {
237                myParent = theParent;
238                myParentPid = theParent.getId();
239                myPartitionIdValue = theParent.getPartitionId().getPartitionId();
240                getPid().myPartitionIdValue = myPartitionIdValue;
241                return this;
242        }
243
244        public TermConceptParentChildLink setRelationshipType(RelationshipTypeEnum theRelationshipType) {
245                myRelationshipType = theRelationshipType;
246                return this;
247        }
248
249        @Override
250        public String toString() {
251                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
252                                .append("pid", myId)
253                                .append("csvPid", myCodeSystemVersionPid)
254                                .append("parentPid", myParentPid)
255                                .append("childPid", myChildPid)
256                                .append("rel", myRelationshipType)
257                                .toString();
258        }
259
260        public enum RelationshipTypeEnum {
261                // ********************************************
262                // IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
263                ISA
264        }
265
266        @Embeddable
267        public static class TermConceptParentChildLinkPk {
268
269                @SequenceGenerator(name = "SEQ_CONCEPT_PC_PID", sequenceName = "SEQ_CONCEPT_PC_PID")
270                @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PC_PID")
271                @Column(name = "PID")
272                private Long myId;
273
274                @PartitionedIdProperty
275                @Column(name = PartitionablePartitionId.PARTITION_ID, nullable = false)
276                private Integer myPartitionIdValue;
277
278                @Override
279                public int hashCode() {
280                        return Objects.hash(myId, myPartitionIdValue);
281                }
282
283                @Override
284                public boolean equals(Object theO) {
285                        if (this == theO) {
286                                return true;
287                        }
288                        if (!(theO instanceof TermConceptParentChildLinkPk)) {
289                                return false;
290                        }
291                        TermConceptParentChildLinkPk that = (TermConceptParentChildLinkPk) theO;
292                        return Objects.equals(myId, that.myId) && Objects.equals(myPartitionIdValue, that.myPartitionIdValue);
293                }
294
295                @Override
296                public String toString() {
297                        return myPartitionIdValue + "/" + myId;
298                }
299        }
300}