caruby-tissue 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/bin/crtdump +11 -3
- data/bin/{seed → crtseed} +0 -0
- data/conf/annotation/pathology_scg/AdditionalFinding.hbm.xml +19 -0
- data/conf/annotation/pathology_scg/BasePathologyAnnotation.hbm.xml +260 -0
- data/conf/annotation/pathology_scg/BreastMargin.hbm.xml +21 -0
- data/conf/annotation/pathology_scg/BreastMarginInvolved.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/BreastMarginUninvolved.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/CNSMargin.hbm.xml +19 -0
- data/conf/annotation/pathology_scg/CNSMarginLocation.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/CarcinomaInSituStatus.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/ColorectalLocalExcisionMarginUninvolved.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/ColorectalResectedMarginUninvolved.hbm.xml +22 -0
- data/conf/annotation/pathology_scg/Cytogenetics.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/DeepMelanomaMargin.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/Details.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/DirectExtensionOfTumor.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/DistalMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/DistanceFromAnalVerge.hbm.xml +23 -0
- data/conf/annotation/pathology_scg/DistantMetastasis.hbm.xml +19 -0
- data/conf/annotation/pathology_scg/ExcionalBiopsyMarginUninvolved.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/ExcisionalBiopsyColorectalDeepMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/ExcisionalBiopsyColorectalLateralOrMucosalMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/ExtraprostaticExtension.hbm.xml +20 -0
- data/conf/annotation/pathology_scg/ExtraprostaticExtensionTissueSites.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/GleasonScore.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/HistologicGrade.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/HistologicType.hbm.xml +19 -0
- data/conf/annotation/pathology_scg/HistologicVariantType.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/ImmunoPhenotyping.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/Invasion.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/KidneyMarginLocation.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/KidneyNephrectomyMargin.hbm.xml +19 -0
- data/conf/annotation/pathology_scg/LateralMelanomaMargin.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/LocalExcisionColorectalDeepMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/LocalExcisionColorectalLateralMargin.hbm.xml +20 -0
- data/conf/annotation/pathology_scg/LungResectionMargin.hbm.xml +17 -0
- data/conf/annotation/pathology_scg/LungResectionMarginsUninvolved.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/MacroscopicExtentOfTumor.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/MesentricMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/MetastasisTissueSite.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/Microcalcification.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/NottinghamHistologicScore.hbm.xml +17 -0
- data/conf/annotation/pathology_scg/OtherResectedOrgans.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/PancreasMargin.hbm.xml +20 -0
- data/conf/annotation/pathology_scg/PancreasMarginInvolvedByInvasiveCarcinoma.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/PancreasMarginUninvolvedByInvasiveCarcinoma.hbm.xml +20 -0
- data/conf/annotation/pathology_scg/PathologicalStaging.hbm.xml +16 -0
- data/conf/annotation/pathology_scg/PolypConfiguration.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/PrimaryTumorStage.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/ProstateMarginLocation.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/ProximalMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/RadialMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/RadicalProstatectomyMargin.hbm.xml +20 -0
- data/conf/annotation/pathology_scg/RegionalLymphNode.hbm.xml +19 -0
- data/conf/annotation/pathology_scg/SatelliteNodule.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/SpecimenCollectionGroup.hbm.xml +87 -0
- data/conf/annotation/pathology_scg/SpecimenIntegrity.hbm.xml +15 -0
- data/conf/annotation/pathology_scg/SpecimenSize.hbm.xml +17 -0
- data/conf/annotation/pathology_scg/TissueSide.hbm.xml +14 -0
- data/conf/annotation/pathology_scg/TumorSize.hbm.xml +29 -0
- data/conf/annotation/pathology_scg/TumorTissueSite.hbm.xml +20 -0
- data/conf/annotation/pathology_scg/UninvolvedMelanomaMargin.hbm.xml +15 -0
- data/conf/annotation/pathology_specimen/AdditionalFinding.hbm.xml +19 -0
- data/conf/annotation/pathology_specimen/AdditionalPathologicFinding.hbm.xml +18 -0
- data/conf/annotation/pathology_specimen/Details.hbm.xml +15 -0
- data/conf/annotation/pathology_specimen/GleasonScore.hbm.xml +16 -0
- data/conf/annotation/pathology_specimen/HistologicGrade.hbm.xml +16 -0
- data/conf/annotation/pathology_specimen/HistologicType.hbm.xml +19 -0
- data/conf/annotation/pathology_specimen/HistologicVariantType.hbm.xml +14 -0
- data/conf/annotation/pathology_specimen/Invasion.hbm.xml +16 -0
- data/conf/annotation/pathology_specimen/NottinghamHistologicScore.hbm.xml +17 -0
- data/conf/annotation/pathology_specimen/Specimen.hbm.xml +52 -0
- data/conf/annotation/pathology_specimen/SpecimenBaseSolidTissuePathologyAnnotation.hbm.xml +73 -0
- data/examples/galena/lib/galena/cli/seed.rb +0 -21
- data/examples/galena/lib/galena/migration/frozen_shims.rb +6 -5
- data/examples/galena/lib/galena/seed/defaults.rb +0 -5
- data/{lib → examples/galena/lib}/galena.rb +0 -0
- data/lib/catissue/annotation/annotatable.rb +37 -0
- data/lib/catissue/annotation/annotatable_class.rb +255 -0
- data/lib/catissue/annotation/annotation.rb +49 -0
- data/lib/catissue/annotation/annotation_class.rb +277 -0
- data/lib/catissue/annotation/annotation_module.rb +77 -0
- data/lib/catissue/annotation/hibernate_mapping.rb +46 -0
- data/lib/catissue/annotation/proxy.rb +28 -0
- data/lib/catissue/annotation/proxy_class.rb +68 -0
- data/lib/catissue/cli/migrate.rb +2 -2
- data/lib/catissue/cli/smoke.rb +6 -4
- data/lib/catissue/database/annotation/annotation_service.rb +75 -61
- data/lib/catissue/database/annotation/annotator.rb +17 -76
- data/lib/catissue/database/annotation/entity_facade.rb +265 -0
- data/lib/catissue/database/annotation/id_generator.rb +62 -0
- data/lib/catissue/database/annotation/integration_service.rb +105 -59
- data/lib/catissue/database/annotation/reference_writer.rb +150 -0
- data/lib/catissue/database/controlled_values.rb +12 -12
- data/lib/catissue/database.rb +148 -58
- data/lib/catissue/domain/abstract_specimen.rb +40 -14
- data/lib/catissue/domain/abstract_specimen_collection_group.rb +1 -3
- data/lib/catissue/domain/collection_protocol.rb +13 -5
- data/lib/catissue/domain/collection_protocol_event.rb +1 -14
- data/lib/catissue/domain/consent_tier_response.rb +2 -0
- data/lib/catissue/domain/consent_tier_status.rb +5 -3
- data/lib/catissue/domain/container.rb +14 -10
- data/lib/catissue/domain/container_position.rb +8 -0
- data/lib/catissue/domain/container_type.rb +13 -6
- data/lib/catissue/domain/participant.rb +15 -10
- data/lib/catissue/domain/site.rb +9 -3
- data/lib/catissue/domain/specimen.rb +79 -40
- data/lib/catissue/domain/specimen_array.rb +11 -1
- data/lib/catissue/domain/specimen_collection_group.rb +79 -41
- data/lib/catissue/domain/specimen_event_parameters.rb +5 -8
- data/lib/catissue/domain/specimen_position.rb +0 -2
- data/lib/catissue/domain/specimen_requirement.rb +1 -1
- data/lib/catissue/domain/storage_container.rb +109 -48
- data/lib/catissue/domain/storage_type.rb +1 -1
- data/lib/catissue/migration/migrator.rb +6 -14
- data/lib/catissue/resource.rb +18 -8
- data/lib/catissue/util/position.rb +11 -1
- data/lib/catissue/util/storable.rb +18 -11
- data/lib/catissue/util/storage_type_holder.rb +44 -6
- data/lib/catissue/version.rb +1 -1
- metadata +86 -35
- data/bin/migrate.rb +0 -42
- data/bin/seed.rb +0 -43
- data/examples/galena/doc/CaTissue/Participant.html +0 -241
- data/examples/galena/doc/CaTissue/SpecimenCollectionGroup.html +0 -190
- data/examples/galena/doc/CaTissue/StorageContainer.html +0 -179
- data/examples/galena/doc/CaTissue/TissueSpecimen.html +0 -320
- data/examples/galena/doc/CaTissue.html +0 -93
- data/examples/galena/doc/Galena/Seed/Defaults.html +0 -650
- data/examples/galena/doc/Galena/Seed.html +0 -203
- data/examples/galena/doc/Galena.html +0 -172
- data/examples/galena/doc/_index.html +0 -181
- data/examples/galena/doc/class_list.html +0 -36
- data/examples/galena/doc/css/common.css +0 -1
- data/examples/galena/doc/css/full_list.css +0 -53
- data/examples/galena/doc/css/style.css +0 -307
- data/examples/galena/doc/file.README.html +0 -153
- data/examples/galena/doc/file_list.html +0 -38
- data/examples/galena/doc/frames.html +0 -13
- data/examples/galena/doc/index.html +0 -153
- data/examples/galena/doc/js/app.js +0 -202
- data/examples/galena/doc/js/full_list.js +0 -149
- data/examples/galena/doc/js/jquery.js +0 -154
- data/examples/galena/doc/method_list.html +0 -163
- data/examples/galena/doc/top-level-namespace.html +0 -112
- data/lib/README.html +0 -33
- data/lib/catissue/database/annotation/annotatable_service.rb +0 -25
- data/lib/catissue/database/annotation/entity_manager.rb +0 -10
- data/lib/galena/cli/seed.rb +0 -43
- data/lib/galena/migration/filter_shims.rb +0 -43
- data/lib/galena/migration/frozen_shims.rb +0 -53
- data/lib/galena/seed/defaults.rb +0 -109
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'caruby/import/java'
|
3
|
+
require 'caruby/database/sql_executor'
|
4
|
+
require 'catissue/database/annotation/id_generator'
|
5
|
+
|
6
|
+
module CaTissue
|
7
|
+
module Annotation
|
8
|
+
# Import this EntityManager dependency before EntityManager.
|
9
|
+
java_import('edu.wustl.common.security.exceptions.UserNotAuthorizedException')
|
10
|
+
|
11
|
+
# Import the caTissue Java EntityManager.
|
12
|
+
java_import('edu.common.dynamicextensions.entitymanager.EntityManager')
|
13
|
+
|
14
|
+
# EntityFacade is the caRuby interface to the caTissue EntityManager. EntityManager is
|
15
|
+
# the caTissue singleton Winnebago object for doing lots of things with dynamic extensions.
|
16
|
+
class EntityFacade
|
17
|
+
include Singleton
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Initializes the caTissue EntityManager, an id generator and a SQL executor. The id
|
22
|
+
# generator and executor are used for the caTissue bug work-arounds described in the
|
23
|
+
# method docs and {IdGenerator}.
|
24
|
+
def initialize
|
25
|
+
# the encapsulated caTissue singleton
|
26
|
+
@emgr = EntityManager.instance
|
27
|
+
# the work-around id generator
|
28
|
+
@idgen = IdGenerator.new
|
29
|
+
# a general-purpose SQL executor for calling the work-arounds
|
30
|
+
@executor = CaRuby::SQLExecutor.new(CaTissue.access_properties)
|
31
|
+
# the primary entity class => entity id hash
|
32
|
+
@pr_eid_hash = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
public
|
36
|
+
|
37
|
+
# @param [Annotation] the annotation object
|
38
|
+
# @return [Integer] a new identifier for the given annotation object
|
39
|
+
def next_identifier(annotation)
|
40
|
+
# Commented line is broken - see IdGenerator doc.
|
41
|
+
# EntityManager.instance.getNextIdentifierForEntity(annotation.class.name.demodulize)
|
42
|
+
|
43
|
+
# The entity table name, which will be a cryptic value like DE_E_1283.
|
44
|
+
eid = primary_entity_id(annotation.class)
|
45
|
+
aeid = common_ancestor_entity_id(eid)
|
46
|
+
tbl = annotation_table_for_entity_id(aeid)
|
47
|
+
next_identifier_for_table(tbl)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [String] the table name
|
51
|
+
# @return [Integer] the next identifier to use when creating a table record
|
52
|
+
def next_identifier_for_table(table)
|
53
|
+
# delegate to id generator
|
54
|
+
@idgen.next_identifier(table)
|
55
|
+
end
|
56
|
+
|
57
|
+
# caTissue alert - unlike the hook entity id lookup, the annotation entity id lookup strips the leading
|
58
|
+
# package prefix from the annotation class name. caTissue DE API requires this undocumented inconsistency.
|
59
|
+
#
|
60
|
+
# caTissue alert - call into caTissue to get entity id doesn't work. caRuby uses direct SQL instead.
|
61
|
+
#
|
62
|
+
# @param [Class] klass the {Annotation} primary class
|
63
|
+
# @param [Boolean] validate flag indicating whether to raise an exception if the class is not primary
|
64
|
+
# @return [Integer] the caTissue entity id for the class
|
65
|
+
# @raise [AnnotationError] if the validate flag is set and the class is not primary
|
66
|
+
def primary_entity_id(klass, validate=true)
|
67
|
+
eid = @pr_eid_hash[klass] ||= recursive_primary_entity_id(klass)
|
68
|
+
if eid.nil? and validate then raise AnnotationError.new("Entity not found for annotation #{klass}") end
|
69
|
+
eid
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param [Class] klass the {Annotatable} class
|
73
|
+
# @return [Integer] the class entity id
|
74
|
+
def hook_entity_id(klass)
|
75
|
+
entity_id_for_class_designator(klass.java_class.name)
|
76
|
+
end
|
77
|
+
|
78
|
+
# caTissue alert - call into caTissue to get entity id doesn't work for non-primary object.
|
79
|
+
# Furthermore, the SQL used for the #{#primary_entity_id} doesn't work for associated annotation
|
80
|
+
# classes. Use alternative SQL instead.
|
81
|
+
#
|
82
|
+
# @param [Integer] eid the referencing entity id
|
83
|
+
# @param [String] eid the association property name
|
84
|
+
# @return [Integer] the referenced {Annotation} class entity id
|
85
|
+
def associated_entity_id(eid, name)
|
86
|
+
# The caTissue role is capitalized.
|
87
|
+
role = name.capitalize_first
|
88
|
+
ref_eid = recursive_associated_entity_id(eid, role)
|
89
|
+
if ref_eid then
|
90
|
+
logger.debug { "Entity id #{eid} is associated with property #{name} via entity id #{ref_eid}." }
|
91
|
+
else
|
92
|
+
logger.debug { "Entity id #{eid} is not associated with property #{name}." }
|
93
|
+
end
|
94
|
+
ref_eid
|
95
|
+
end
|
96
|
+
|
97
|
+
# caTissue alert - Annotation classes are incorrectly mapped to entity ids, which in turn are
|
98
|
+
# incorrectly mapped to a table name. A candidate work-around is to bypass the caTissue DE
|
99
|
+
# mechanism and hit the DE Hibernate config files directly. However, the DE Hibernate mappings
|
100
|
+
# are incorrect and possibly no longer used. Therefore, the table must be obtained by SQL
|
101
|
+
# work-arounds.
|
102
|
+
#
|
103
|
+
# @param [Annotation] obj the annotation object
|
104
|
+
# @return [String] the entity table name
|
105
|
+
# @param [Integer] the annotation entity identifier
|
106
|
+
# @return [String] the entity table name
|
107
|
+
def annotation_table_for_entity_id(eid)
|
108
|
+
result = @executor.execute { |dbh| dbh.select_one(TABLE_NAME_SQL, eid) }
|
109
|
+
if result.nil? then raise AnnotationError.new("Table not found for annotation entity id #{eid}") end
|
110
|
+
tbl = result[0]
|
111
|
+
logger.debug { "Annotation entity with id #{eid} has table #{tbl}." }
|
112
|
+
tbl
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param (see #associated_entity_id)
|
116
|
+
# @return [Integer, nil] the parent entity id, if any
|
117
|
+
def parent_entity_id(eid)
|
118
|
+
result = @executor.execute { |dbh| dbh.select_one(PARENT_ENTITY_ID_SQL, eid) }
|
119
|
+
result[0] if result
|
120
|
+
end
|
121
|
+
|
122
|
+
# Obtains the undocumented caTisue container id for the given primary entity id.
|
123
|
+
#
|
124
|
+
# caTissue alert - EntityManager.getContainerIdForEntitycontainer uses incorrect table
|
125
|
+
# (cf. https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=421&sid=5252d951301e598eebf3e90036da43cb).
|
126
|
+
# The standard DE API call submits the query:
|
127
|
+
# SELECT IDENTIFIER FROM dyextn_container WHERE ENTITY_ID = ?
|
128
|
+
# This results in the error:
|
129
|
+
# Unknown column 'ENTITY_ID' in 'where clause'
|
130
|
+
# The correct SQL is as follows:
|
131
|
+
# SELECT IDENTIFIER FROM dyextn_container WHERE ABSTRACT_ENTITY_ID = ?
|
132
|
+
# The work-around is to call this SQL directly.
|
133
|
+
#
|
134
|
+
# @return [Integer] eid the primary entity id
|
135
|
+
# @raise [AnnotationError] if no container id is found
|
136
|
+
def container_id(eid)
|
137
|
+
# The following call is broken (see method doc).
|
138
|
+
# EntityManager.instance.get_container_id_for_entity(eid)
|
139
|
+
# Work-around caTissue bug with direct query.
|
140
|
+
result = @executor.execute { |dbh| dbh.select_one(CTR_ID_SQL, eid) }
|
141
|
+
cid = result[0].to_i if result
|
142
|
+
if cid.nil? then
|
143
|
+
raise AnnotationError.new("Dynamic extension container id not found for annotation #{annotation} with entity id #{eid}")
|
144
|
+
end
|
145
|
+
logger.debug { "Annotation with entity id #{eid} has container id #{cid}." }
|
146
|
+
cid
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
# @param (see #primary_entity_id)
|
152
|
+
# @return (see #primary_entity_id)
|
153
|
+
def recursive_primary_entity_id(klass)
|
154
|
+
eid = nonrecursive_primary_entity_id(klass) || parent_primary_entity_id(klass)
|
155
|
+
if eid then logger.debug { "#{klass.qp} has entity id #{eid}." } end
|
156
|
+
eid
|
157
|
+
end
|
158
|
+
|
159
|
+
# @param (see #primary_entity_id)
|
160
|
+
# @return (see #primary_entity_id)
|
161
|
+
def nonrecursive_primary_entity_id(klass)
|
162
|
+
# The Java class package is the entity group, the Java class unqualified name is the caption.
|
163
|
+
pkg, cls_nm = klass.java_class.name.split('.')
|
164
|
+
# Dive into some obscure SQL
|
165
|
+
result = @executor.execute { |dbh| dbh.select_one(CTR_ENTITY_ID_SQL, pkg, cls_nm) }
|
166
|
+
result[0] if result
|
167
|
+
end
|
168
|
+
|
169
|
+
# @param (see #primary_entity_id)
|
170
|
+
# @return (see #primary_entity_id)
|
171
|
+
def parent_primary_entity_id(klass)
|
172
|
+
nonrecursive_primary_entity_id(klass.superclass) if klass.superclass < Annotation
|
173
|
+
end
|
174
|
+
|
175
|
+
# @param [Integer] the starting entity id
|
176
|
+
# @return [Integer] the top-most ancestor entity id
|
177
|
+
def common_ancestor_entity_id(eid)
|
178
|
+
peid = parent_entity_id(eid)
|
179
|
+
peid ? common_ancestor_entity_id(peid) : eid
|
180
|
+
end
|
181
|
+
|
182
|
+
# @param eid (see #associated_entity_id)
|
183
|
+
# @param role the property role name
|
184
|
+
# @return [Integer, nil] the associated entity id, if any
|
185
|
+
def recursive_associated_entity_id(eid, role)
|
186
|
+
nonrecursive_associated_entity_id(eid, role) or parent_associated_entity_id(eid, role)
|
187
|
+
end
|
188
|
+
|
189
|
+
# @param (see #recursive_associated_entity_id)
|
190
|
+
# @return [Integer, nil] the associated entity id in the context of the parent, if any
|
191
|
+
def parent_associated_entity_id(eid, role)
|
192
|
+
peid = parent_entity_id(eid) || return
|
193
|
+
logger.debug { "Finding entity id #{eid} #{role} associated entity id using parent entity id #{peid}..." }
|
194
|
+
recursive_associated_entity_id(peid, role)
|
195
|
+
end
|
196
|
+
|
197
|
+
# @param (see #recursive_associated_entity_id)
|
198
|
+
# @return @return [Integer, nil] the directly associated entity id, if any
|
199
|
+
def nonrecursive_associated_entity_id(eid, role)
|
200
|
+
logger.debug { "Finding entity id #{eid} #{role} associated entity id..." }
|
201
|
+
result = @executor.execute { |dbh| dbh.select_one(ASSN_ENTITY_ID_SQL, eid, role) }
|
202
|
+
# The role role can be a mutation of the property name with spaces inserted in the
|
203
|
+
# camel-case components, e.g. 'Additional Finding' instead of 'AdditionalFinding'.
|
204
|
+
# TODO - fix this kludge by finding out how the role relates to the property in the
|
205
|
+
# database.
|
206
|
+
if result.nil? and role =~ /.+[A-Z]/ then
|
207
|
+
alt = role.gsub(/(.)([A-Z])/, '\1 \2')
|
208
|
+
logger.debug { "Attempting to find entity id #{eid} #{role} associated entity id using variant #{alt}..." }
|
209
|
+
result = @executor.execute { |dbh| dbh.select_one(ASSN_ENTITY_ID_SQL, eid, alt) }
|
210
|
+
end
|
211
|
+
if result.nil? and role =~ /[pP]athologic[^a]/ then
|
212
|
+
alt = role.sub(/([pP])athologic/, '\1athological')
|
213
|
+
logger.debug { "Attempting to find entity id #{eid} #{role} associated entity id using variant #{alt}..." }
|
214
|
+
result = @executor.execute { |dbh| dbh.select_one(ASSN_ENTITY_ID_SQL, eid, alt) }
|
215
|
+
end
|
216
|
+
if result.nil? then
|
217
|
+
logger.debug { "Entity id #{eid} is not directly associated with #{role}." }
|
218
|
+
end
|
219
|
+
result[0] if result
|
220
|
+
end
|
221
|
+
|
222
|
+
# @param [String] designator the class name, demodulized in the case of an annotation entity
|
223
|
+
# @return [Integer] the caTissue entity id for the given class name
|
224
|
+
# @raise [CaRuby::DatabaseError] if the DE entity id is not found for the given designator
|
225
|
+
def entity_id_for_class_designator(designator)
|
226
|
+
@emgr.getEntityId(designator) or
|
227
|
+
raise CaRuby::DatabaseError.new("Dynamic extension entity id not found for #{designator}")
|
228
|
+
end
|
229
|
+
|
230
|
+
# The SQL to find an entity id for a primary entity.
|
231
|
+
CTR_ENTITY_ID_SQL = <<EOS
|
232
|
+
select ctr.ABSTRACT_ENTITY_ID
|
233
|
+
from DYEXTN_CONTAINER ctr, DYEXTN_ENTITY_GROUP grp
|
234
|
+
where ctr.ENTITY_GROUP_ID = grp.IDENTIFIER
|
235
|
+
and grp.SHORT_NAME = ?
|
236
|
+
and ctr.CAPTION = ?
|
237
|
+
EOS
|
238
|
+
|
239
|
+
# The SQL to find an entity id for a secondary annotation referenced by a primary annotation.
|
240
|
+
ASSN_ENTITY_ID_SQL = <<EOS
|
241
|
+
select assn.TARGET_ENTITY_ID
|
242
|
+
from DYEXTN_ATTRIBUTE attr, DYEXTN_ABSTRACT_ENTITY ae, DYEXTN_ASSOCIATION assn, DYEXTN_ROLE role
|
243
|
+
where assn.IDENTIFIER = attr.IDENTIFIER
|
244
|
+
and attr.ENTIY_ID = ae.id
|
245
|
+
and assn.TARGET_ROLE_ID = role.IDENTIFIER
|
246
|
+
and ae.id = ?
|
247
|
+
and role.name = ?
|
248
|
+
EOS
|
249
|
+
|
250
|
+
# The SQL to find a parent entity id for a given entity id.
|
251
|
+
PARENT_ENTITY_ID_SQL = 'select e.parent_entity_id from DYEXTN_ENTITY e where e.identifier = ?'
|
252
|
+
|
253
|
+
# The SQL to find the database table for an entity id.
|
254
|
+
TABLE_NAME_SQL = <<EOS
|
255
|
+
select dp.name
|
256
|
+
from dyextn_database_properties dp, dyextn_table_properties tp
|
257
|
+
where dp.identifier = tp.identifier
|
258
|
+
and tp.abstract_entity_id = ?
|
259
|
+
EOS
|
260
|
+
|
261
|
+
# The caTissue DE API container id bug work-around query
|
262
|
+
CTR_ID_SQL = "select identifier from dyextn_container where abstract_entity_id = ?"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'caruby/database/sql_executor'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
module Annotation
|
5
|
+
# The IdGenerator delegates to the caTissue entity manager to create a new identifier for an annotation.
|
6
|
+
class IdGenerator
|
7
|
+
def initialize
|
8
|
+
@executor = CaRuby::SQLExecutor.new(CaTissue.access_properties)
|
9
|
+
end
|
10
|
+
|
11
|
+
# caTissue alert - DE API subquery search fails.
|
12
|
+
# EntityManager.getNextIdentifierForEntity(EntityManager.java:2689) returns zero for some, but not all, DEs.
|
13
|
+
# This is a cascading error that is difficult to trace. The EntityManager.getNextIdentifierForEntity error
|
14
|
+
# is printed to the console rather than propagated up the call stack. A subsequent create then fails because
|
15
|
+
# the identifier is not set.
|
16
|
+
#
|
17
|
+
# A candidate work-around is to resolve the DE table name by issuing a direct SQL call and pass this
|
18
|
+
# to DE API EntityManagerUtil.getNextIdentifier. However, EntityManagerUtil is broken as well, for a
|
19
|
+
# different reason.
|
20
|
+
#
|
21
|
+
# Another candidate work-around is to get the next id manually from the caTissue DYEXTN_ID_GENERATOR table.
|
22
|
+
# However, this table is suspect since it is not defined as a database sequence generator and is not
|
23
|
+
# referenced in the caTissue source code. It is not used by the caTissue GUI when creating an annotation.
|
24
|
+
#
|
25
|
+
# The work-around to the work-around to the work-around is to call the following SQL directly:
|
26
|
+
# select max(identifier) from <table>
|
27
|
+
# where \<table\> is the result of the EntityManager work-around query. The EntityManager work-around query
|
28
|
+
# is described in
|
29
|
+
# https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=404&p=2785&sid=febe0a1271b3d00020927741a94e9bff#p2785.
|
30
|
+
#
|
31
|
+
# Unfortunately, the +select max+ work-around is hampered by the obvious concurrent access race condition.
|
32
|
+
# For an unknown reason, caTissue DE does not use database sequence generators to make DE identifiers.
|
33
|
+
# Even if the caTissue DE API worked, it might suffer from the same race condition. The DE API
|
34
|
+
# EntityManagerUtil caches identifiers. The API call is synchronized, but it is unclear whether each DE
|
35
|
+
# client accesses the same EntityManagerUtil instance as the GUI DE action processor. If not, then the
|
36
|
+
# caTissue DE API, assuming that it were functional, would be open to an even more serious concurrency
|
37
|
+
# conflict than the work-around race condition.
|
38
|
+
#
|
39
|
+
# @param [String] the entity table
|
40
|
+
# @return [Integer] a new identifier for the given annotation object
|
41
|
+
def next_identifier(table)
|
42
|
+
# Commented line is broken - see method doc.
|
43
|
+
# The caTissue EntityManagerUtil hands out table record ids in the work-around.
|
44
|
+
#identifier = EntityManagerUtil.getNextIdentifier(table)
|
45
|
+
|
46
|
+
logger.debug { "Work around caTissue DE EntityManagerUtil bug by fetching the maximum #{table} identifier directly from the database..." }
|
47
|
+
sql = TABLE_MAX_ID_SQL_TMPL % table
|
48
|
+
result = @executor.execute { |dbh| dbh.select_one(sql) }
|
49
|
+
max = result ? result[0].to_i : 0
|
50
|
+
identifier = max + 1
|
51
|
+
# End of work-around
|
52
|
+
|
53
|
+
logger.debug { "Next #{table} identifier is #{identifier}." }
|
54
|
+
identifier
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
TABLE_MAX_ID_SQL_TMPL = "select max(identifier) from %s"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -2,85 +2,131 @@ require 'caruby/database/persistence_service'
|
|
2
2
|
|
3
3
|
module CaTissue
|
4
4
|
module Annotation
|
5
|
-
# An IntegrationService fetches and saves CaTissue
|
5
|
+
# An IntegrationService fetches and saves CaTissue hook-annotation associations.
|
6
6
|
class IntegrationService < CaRuby::PersistenceService
|
7
7
|
SERVICE_NAME = 'deintegration'
|
8
8
|
|
9
9
|
java_import('deintegration.EntityMap')
|
10
|
+
|
10
11
|
java_import('deintegration.EntityMapRecord')
|
12
|
+
|
13
|
+
java_import('deintegration.FormContext')
|
11
14
|
|
12
|
-
|
15
|
+
# @param [EntityFacade] the global entity manager
|
16
|
+
def initialize
|
13
17
|
super(SERVICE_NAME)
|
14
|
-
|
18
|
+
# SQL executor to handle caTissue DE API bug work-around
|
19
|
+
@executor = CaRuby::SQLExecutor.new(CaTissue.access_properties)
|
15
20
|
end
|
16
21
|
|
17
|
-
# Associates the given
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
# Associates the given hook domain object to the annotation.
|
23
|
+
#
|
24
|
+
# @param [Annotatable] hook the hook entity
|
25
|
+
# @param [Annotation] annotation the annotation entity
|
26
|
+
def associate(hook, annotation)
|
27
|
+
logger.debug { "Associating annotation #{annotation} to owner #{hook}..." }
|
28
|
+
emr = create_entity_map_record(hook, annotation)
|
29
|
+
create(emr)
|
22
30
|
end
|
23
31
|
|
24
|
-
# Removes the existing association between the given
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
32
|
+
# # Removes the existing association between the given hook domain object to annotation.
|
33
|
+
# #
|
34
|
+
# # @param (see #associate)
|
35
|
+
# def dissociate(hook, annotation)
|
36
|
+
# assn = find_entity_map_record(hook, annotation) # TODO - implement
|
37
|
+
# delete(assn)
|
38
|
+
# end
|
29
39
|
|
30
40
|
private
|
31
41
|
|
32
|
-
|
33
|
-
ENTITY_MAP_BUG = true
|
34
|
-
|
35
|
-
## The cruft below is adapted from caTissue ClientDemo_SCG.java and cleaned up (but still obscure). ##
|
36
|
-
|
37
|
-
def create_entity_map_record(anchor, annotation)
|
38
|
-
record = EntityMapRecord.new
|
39
|
-
raise CaRuby::DatabaseError.new("Annotation entity map static entity does not have an identifier: #{anchor}") if anchor.identifier.nil?
|
40
|
-
record.static_entity_record_id = anchor.identifier
|
41
|
-
raise CaRuby::DatabaseError.new("Annotation entity map dynamic entity does not have an identifier: #{annotation}") if annotation.identifier.nil?
|
42
|
-
record.dynamic_entity_record_id = annotation.identifier
|
43
|
-
record.form_context = form_context(anchor, annotation)
|
44
|
-
record.form_context_id = record.form_context.identifier if record.form_context
|
45
|
-
record
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the undocumented caTissue FormContext, which might be another bit of caTissue presentation
|
49
|
-
# flotsam polluting the data layer.
|
50
|
-
def form_context(anchor, annotation)
|
51
|
-
map = EntityMap.new
|
52
|
-
map.static_entity_id = anchor_entity_id(anchor)
|
53
|
-
return if ENTITY_MAP_BUG
|
54
|
-
# 2 bugs:
|
55
|
-
# * caTissue bug - bad container id query, cf. https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=421&sid=5252d951301e598eebf3e90036da43cb
|
56
|
-
# * jRuby bug? - query on map call to map.pp_s wipes out the map java_class! Can't step into pp_s with debugger so can't isolate cause without more work
|
57
|
-
# TODO - isolate and file both bugs
|
58
|
-
#map.container_id = entity_container_id(annotation)
|
59
|
-
map.form_context_collection.first if query(map)
|
60
|
-
end
|
42
|
+
#### The cruft below is adapted from caTissue ClientDemo_SCG.java and cleaned up (but still obscure). ####
|
61
43
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
44
|
+
# Creates an entity map record with content (annotation entity id, annotation id, form context id).
|
45
|
+
# This record associates the static hook record to the annotation record qualified by the context.
|
46
|
+
#
|
47
|
+
# @param (see #associate)
|
48
|
+
# @return [EntityMapRecord] the new entity map record
|
49
|
+
def create_entity_map_record(hook, annotation)
|
50
|
+
# the entity map record with content (annotation entity id, annotation id, context id)
|
51
|
+
emr = EntityMapRecord.new
|
52
|
+
|
53
|
+
# the hook id
|
54
|
+
if hook.identifier.nil? then
|
55
|
+
raise CaRuby::DatabaseError.new("Annotation entity map static entity does not have an identifier: #{hook}")
|
56
|
+
end
|
57
|
+
emr.static_entity_record_id = hook.identifier
|
58
|
+
|
59
|
+
# the annotation id
|
60
|
+
if annotation.identifier.nil? then
|
61
|
+
raise CaRuby::DatabaseError.new("Annotation entity map dynamic entity does not have an identifier: #{annotation}")
|
62
|
+
end
|
63
|
+
emr.dynamic_entity_record_id = annotation.identifier
|
64
|
+
|
65
|
+
# the form context
|
66
|
+
ctxt = form_context(hook, annotation)
|
67
|
+
if ctxt then
|
68
|
+
emr.form_context = ctxt
|
69
|
+
emr.form_context_id = ctxt.id
|
70
|
+
end
|
71
|
+
|
72
|
+
emr
|
67
73
|
end
|
68
74
|
|
69
|
-
#
|
70
|
-
|
71
|
-
|
75
|
+
# @param (see #associate)
|
76
|
+
# @return [FormContent] an undocumented bit of caTissue presentation flotsam polluting the data layer
|
77
|
+
def form_context(hook, annotation)
|
78
|
+
map = entity_map(hook, annotation)
|
79
|
+
|
80
|
+
# the fetched form context
|
81
|
+
ctxts = map.form_context_collection
|
82
|
+
if ctxts.empty? then
|
83
|
+
logger.debug { "#{hook} entity map #{map.qp} does not have a form context." }
|
84
|
+
elsif ctxts.size > 1 then
|
85
|
+
ctxt_ids = ctxts.map { |ctxt| ctxt.id }
|
86
|
+
raise CaRuby::DatabaseError.new("More than one form context for #{hook} - form context ids: #{ctxt_ids.qp}")
|
87
|
+
else
|
88
|
+
ctxt = ctxts.first
|
89
|
+
logger.debug { "#{hook} has form context id #{ctxt.id}." }
|
90
|
+
end
|
91
|
+
|
92
|
+
ctxt
|
72
93
|
end
|
73
|
-
|
74
|
-
#
|
94
|
+
|
95
|
+
# caTissue alert - the entity map is associated with a domain class in the hook class hierarchy.
|
96
|
+
# The generic approach to determining the entity map for a given hook object and annotation object
|
97
|
+
# is to iterate over the hook class hierarchy until a matching ENTITY_MAP record is found for the
|
98
|
+
# hook class ancestor and the annotation container id.
|
75
99
|
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
def
|
79
|
-
|
100
|
+
# @param (see #associate)
|
101
|
+
# @return [EntityMap] the entity map
|
102
|
+
def entity_map(hook, annotation)
|
103
|
+
klass = hook.class
|
104
|
+
while klass < CaTissue::AbstractDomainObject
|
105
|
+
map = entity_map_for_class(klass, annotation)
|
106
|
+
return map if map
|
107
|
+
klass = klass.superclass
|
108
|
+
end
|
109
|
+
nil
|
80
110
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
111
|
+
|
112
|
+
# @param [Class] klass the hook class
|
113
|
+
# @param annotation (see #entity_map)
|
114
|
+
# @return (see #entity_map)
|
115
|
+
def entity_map_for_class(klass, annotation)
|
116
|
+
# A query template
|
117
|
+
tmpl = EntityMap.new
|
118
|
+
tmpl.static_entity_id = klass.effective_entity_id
|
119
|
+
# the container id
|
120
|
+
tmpl.container_id = annotation.class.container_id
|
121
|
+
# the database record matching the template
|
122
|
+
logger.debug { "Fetching the ENTITY_MAP record for #{klass.qp} entity id #{tmpl.static_entity_id} and container id #{tmpl.container_id}..." }
|
123
|
+
map = query(tmpl).first
|
124
|
+
if map then
|
125
|
+
logger.debug { "Entity map found for #{klass.qp} entity id #{tmpl.static_entity_id}, container id #{tmpl.container_id}: #{map.qp}." }
|
126
|
+
else
|
127
|
+
logger.debug { "ENTITY_MAP record not found for #{klass.qp} entity id #{tmpl.static_entity_id}, container id #{tmpl.container_id}." }
|
128
|
+
end
|
129
|
+
map
|
84
130
|
end
|
85
131
|
end
|
86
132
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module CaTissue
|
2
|
+
module Annotation
|
3
|
+
# A ReferenceWriter saves annotations to the database. This is a helper class to work around
|
4
|
+
# caTissue DE API defects. This class infers a direct data mapping by navigating the caTissue
|
5
|
+
# DYEXT tables of the introspected Java annotation class properties.
|
6
|
+
class ReferenceWriter
|
7
|
+
# @param [Integer] eid the referencing annotation entity id
|
8
|
+
# @param [CaRuby::AttributeMetadata] attr_md the annotation attribute metadata of the attribute to save
|
9
|
+
# @param [Integer, nil] assn_eid the referenced annotation entity id
|
10
|
+
def initialize(eid, attr_md, assn_eid=nil)
|
11
|
+
logger.debug { "Mapping annotation #{attr_md.declarer.qp}.#{attr_md} role attributes to database columns..." }
|
12
|
+
efcd = EntityFacade.instance
|
13
|
+
# the referenced annotation entity id
|
14
|
+
assn_eid ||= associated_entity_id(eid, attr_md)
|
15
|
+
# the referenced entity database table
|
16
|
+
@table = efcd.annotation_table_for_entity_id(assn_eid)
|
17
|
+
# map the attribute => column
|
18
|
+
attr_col_hash = map_attributes(attr_md.type, assn_eid)
|
19
|
+
logger.debug { "Annotation #{attr_md.declarer.qp} #{attr_md} maps to #{@table} as #{attr_col_hash.qp}" }
|
20
|
+
# the mapped attributes and columns
|
21
|
+
@attrs, cols = attr_col_hash.to_a.transpose
|
22
|
+
# the SQL parameters clause
|
23
|
+
params = Array.new(cols.size, '?').join(', ')
|
24
|
+
# the create SQL
|
25
|
+
@cr_sql = CREATE_SQL % [@table, cols.join(', '), params]
|
26
|
+
# the update SQL
|
27
|
+
@upd_sql = UPDATE_SQL % [@table, cols.map { |col| "#{col} = ?" }.join(', ')]
|
28
|
+
# the superclass writer for annotations with superclass DE forms
|
29
|
+
@parent = obtain_parent_writer(eid, attr_md)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [Annotation] annotation the referenced annotation value
|
33
|
+
def save(annotation)
|
34
|
+
# select the SQL based on whether this is an update or a create
|
35
|
+
sql = annotation.identifier ? @upd_sql : @cr_sql
|
36
|
+
# allocate a new database identifier
|
37
|
+
annotation.identifier ||= next_identifier
|
38
|
+
# the values to bind to the SQL parameters
|
39
|
+
values = database_parameters(annotation)
|
40
|
+
logger.debug { "Saving #{annotation} to #{@table}..." }
|
41
|
+
# dispatch the SQL update or create statement
|
42
|
+
CaTissue::Database.instance.executor.execute do |dbh|
|
43
|
+
dbh.prepare(sql) { |sth| sth.execute(*values) }
|
44
|
+
end
|
45
|
+
if @parent then
|
46
|
+
logger.debug { "Saving #{annotation} parent entity attributes..." }
|
47
|
+
@parent.save(annotation)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
# @return [Integer] the identifier to use when creating a new annotation instance
|
54
|
+
def next_identifier
|
55
|
+
@parent ? @parent.next_identifier : EntityFacade.instance.next_identifier_for_table(@table)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# @param (see #initialize)
|
61
|
+
# @return [Integer] the entity id for the given attribute role
|
62
|
+
# @raise [AnnotationError] if the associated entity was not found
|
63
|
+
def associated_entity_id(eid, attr_md)
|
64
|
+
EntityFacade.instance.associated_entity_id(eid, attr_md.property_descriptor.name) or
|
65
|
+
raise AnnotationError.new("Associated entity not found for entity #{eid} attribute #{attr_md}")
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param (see #initialize)
|
69
|
+
# @return [Integer, nil] the superclass associated entity id for the given attribute role, or nil if none
|
70
|
+
def obtain_parent_writer(eid, attr_md)
|
71
|
+
# the superclass entity id for annotations with superclass DE forms
|
72
|
+
peid = EntityFacade.instance.parent_entity_id(eid) || return
|
73
|
+
# the associated entity id
|
74
|
+
aeid = EntityFacade.instance.associated_entity_id(peid, attr_md.property_descriptor.name)
|
75
|
+
ReferenceWriter.new(peid, attr_md, aeid) if aeid
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param annotation (see #save)
|
79
|
+
# @return [Array] the save SQL call parameters
|
80
|
+
def database_parameters(annotation)
|
81
|
+
@attrs.map do |attr|
|
82
|
+
value = annotation.send(attr)
|
83
|
+
Annotation === value ? value.identifier : value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def map_attributes(klass, eid)
|
88
|
+
# the non-domain columns
|
89
|
+
hash = klass.nondomain_attributes.to_compact_hash do |attr|
|
90
|
+
nondomain_attribute_column(klass, attr, eid)
|
91
|
+
end
|
92
|
+
# the owner attribute column
|
93
|
+
ownr_attr = klass.owner_attribute
|
94
|
+
if ownr_attr then hash[ownr_attr] = owner_attribute_column(klass, ownr_attr, eid) end
|
95
|
+
hash
|
96
|
+
end
|
97
|
+
|
98
|
+
def nondomain_attribute_column(klass, attribute, eid)
|
99
|
+
if attribute == :identifier then return IDENTIFIER_COL end
|
100
|
+
attr_md = klass.attribute_metadata(attribute)
|
101
|
+
# skip an attribute declared by the superclass
|
102
|
+
return unless attr_md.declarer == klass
|
103
|
+
xctr = CaTissue::Database.instance.executor
|
104
|
+
prop = attr_md.property_descriptor.name
|
105
|
+
logger.debug { "Finding #{klass.qp} #{attribute} column for entity id #{eid} and property #{prop}..." }
|
106
|
+
result = xctr.execute { |dbh| dbh.select_one(NONDOMAIN_COLUMN_SQL, prop, eid) }
|
107
|
+
col = result[0] if result
|
108
|
+
if col.nil? then raise AnnotationError.new("Column not found for #{klass.qp} #{attribute}") end
|
109
|
+
col
|
110
|
+
end
|
111
|
+
|
112
|
+
def owner_attribute_column(klass, attribute, eid)
|
113
|
+
logger.debug { "Finding #{klass.qp} #{attribute} column in the context of entity id #{eid}..." }
|
114
|
+
result = CaTissue::Database.instance.executor.execute { |dbh| dbh.select_one(OWNER_COLUMN_SQL, eid) }
|
115
|
+
col = result[0] if result
|
116
|
+
if col.nil? then raise AnnotationError.new("Column not found for #{klass.qp} owner attribute #{attribute}") end
|
117
|
+
col
|
118
|
+
end
|
119
|
+
|
120
|
+
IDENTIFIER_COL = 'IDENTIFIER'
|
121
|
+
|
122
|
+
# Generic update template.
|
123
|
+
UPDATE_SQL = "update %s set ACTIVITY_STATUS = 'Active', %s"
|
124
|
+
|
125
|
+
# Generic create template.
|
126
|
+
CREATE_SQL = "insert into %s(ACTIVITY_STATUS, %s)\nvalues ('Active', %s)"
|
127
|
+
|
128
|
+
# SQL to get the primitive column name for a given annotation class entity id and Java property name
|
129
|
+
NONDOMAIN_COLUMN_SQL = <<EOS
|
130
|
+
select dbp.name
|
131
|
+
from DYEXTN_ABSTRACT_METADATA amd, DYEXTN_CONTROL ctl, DYEXTN_CONTAINER ctr, DYEXTN_DATABASE_PROPERTIES dbp, DYEXTN_COLUMN_PROPERTIES cp
|
132
|
+
where amd.NAME = ?
|
133
|
+
and ctl.CONTAINER_ID = ctr.IDENTIFIER
|
134
|
+
and amd.IDENTIFIER = ctl.BASE_ABST_ATR_ID
|
135
|
+
and ctl.BASE_ABST_ATR_ID = cp.PRIMITIVE_ATTRIBUTE_ID
|
136
|
+
and dbp.IDENTIFIER = cp.IDENTIFIER
|
137
|
+
and ctr.ABSTRACT_ENTITY_ID = ?
|
138
|
+
EOS
|
139
|
+
|
140
|
+
# SQL to get the annotation reference column name for a given annotation target entity id.
|
141
|
+
# The target entity id is obtained by calling {EntityFacade#associated_entity_id}.
|
142
|
+
OWNER_COLUMN_SQL = <<EOS
|
143
|
+
select cst.TARGET_ENTITY_KEY
|
144
|
+
from DYEXTN_CONSTRAINT_PROPERTIES cst, DYEXTN_ASSOCIATION assn
|
145
|
+
where cst.ASSOCIATION_ID = assn.IDENTIFIER
|
146
|
+
and assn.TARGET_ENTITY_ID = ?
|
147
|
+
EOS
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|