Skip to content

Commit

Permalink
first attempt at automatic deletion taking into account contained ref…
Browse files Browse the repository at this point in the history
…erences.

* necessary to delete the referrers first, as JPA ordering is not guaranteed
* does not deal with the case that the referrer is at different level.
  • Loading branch information
pahjbo committed Nov 21, 2024
1 parent e83ef92 commit 98d8fb5
Show file tree
Hide file tree
Showing 17 changed files with 117 additions and 29 deletions.
5 changes: 4 additions & 1 deletion doc/guide/modelling/TransformingToVODML.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ Creating VO-DML from other Data Models
It is possible to start with an XML schema that can be transferred to VODSL with

```shell
gradle vodmlXsdToVodsl --xsd mymodel.xsd --dsl mymodel.vodsl
gradle vodmlXsdToVodsl --xsd `pwd`/mymodel.xsd --dsl mymodel.vodsl
```

Note that it is necessary to use the full path for the input file.


The transformation does not cope with all of the "styles" XML schema that are possible, so that it is likely that
the generated VODSL will need further hand editing.

4 changes: 2 additions & 2 deletions models/sample/test/lifecycleTest.vo-dml.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<title></title>
<author>pharriso</author>
<version>0.1</version>
<lastModified>2024-10-19T13:44:44Z</lastModified>
<lastModified>2024-11-21T13:44:44Z</lastModified>
<import>
<name>null</name><!--should not be needed in modern vo-dml -->
<url>IVOA-v1.0.vo-dml.xml</url>
Expand Down Expand Up @@ -196,7 +196,7 @@
<vodml-ref>lifecycleTest:ReferredLifeCycle</vodml-ref>
</datatype>
<multiplicity>
<minOccurs>1</minOccurs>
<minOccurs>0</minOccurs>
<maxOccurs>1</maxOccurs>
</multiplicity>
</reference>
Expand Down
2 changes: 1 addition & 1 deletion models/sample/test/lifecycleTest.vodsl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ otype ATest3 { /* fails the "unique composition rule" in schematron if the one
* but this should be unlikely if a contained type instance is created at the same time as its container instance.
*/
contained : Contained @+ as composition ""; //vodsl tooling does not flag this- but the vodml schematron does.
refBad references ReferredLifeCycle ""; //this should be lifecycle warning
refBad @? references ReferredLifeCycle ""; //this should be lifecycle warning
}

otype ATest4 {
Expand Down
4 changes: 2 additions & 2 deletions runtime/java/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
signing
}
group = "org.javastro.ivoa.vo-dml"
version = "0.8.2"
version = "0.8.3"


dependencies {
Expand All @@ -25,7 +25,7 @@ dependencies {

testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
testRuntimeOnly("ch.qos.logback:logback-classic:1.4.7")
testRuntimeOnly("ch.qos.logback:logback-classic:1.4.12")
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
* Created on 29/03/2022 by Paul Harrison ([email protected]).
*/

import jakarta.persistence.EntityManager;

/**
* Interface to implement some common manipulations in JPA.
* This interface will be implemented on ObjectTypes.
*/
public interface JPAManipulationsForObjectType <ID> extends JPAManipulations {

Expand All @@ -13,7 +16,13 @@ public interface JPAManipulationsForObjectType <ID> extends JPAManipulations {
* @return the database ID.
*/
ID getId();



/**
* Delete the entity from the database. This will take into account any ordering necessary because of any contained references.
* If there are no contained references - then this will be equivalent to em.delete(this), however there might be some efficiency built in via bulk deletion.
* @param em the entity manager
*/
void delete(EntityManager em);


}
1 change: 1 addition & 0 deletions tools/Developing.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ of useful functions that can answer model questions such as "has subtypes" defin
*
There is a [sample](./gradletooling/sample) project that acts as a test bench for the plugin.

http://dh.obdurodon.org/xslt3.xhtml is a good summary of the new features in XSLT 3.0.

## Local testing

Expand Down
6 changes: 5 additions & 1 deletion tools/gradletooling/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,8 @@ VODML Tooling TODO
* using dataclasses - need python 3.10 for the kw_only field specifier - might do better just generating multiple `__init__()` rather than relying on the dataclass generation.

# Distribution
* need better directory structure on IVOA site....
* need better directory structure on IVOA site....




Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class VodmlGradlePlugin: Plugin<Project> {


//add the dependencies for JAXB and JPA - using the hibernate implementation
listOf("org.javastro.ivoa.vo-dml:vodml-runtime:0.8.2",
listOf("org.javastro.ivoa.vo-dml:vodml-runtime:0.8.3",
"jakarta.xml.bind:jakarta.xml.bind-api:4.0.0",
"org.glassfish.jaxb:jaxb-runtime:4.0.2",
// "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10", // supports JPA 2.2
Expand Down
2 changes: 1 addition & 1 deletion tools/gradletooling/sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ python {
pip("pytest:7.3.1")
pip("SQLAlchemy:2.0.30")
pip("xsdata[lxml,cli]:24.5")
pip("pydantic:2.7.1")
pip("pydantic:2.9.2")
pip("sqlmodel:0.0.22")
pip("xsdata-pydantic:24.5")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ void setUp() throws Exception {
model = new LifecycleTestModel();
// model.addContent(atest);
model.addContent(atest2);
model.addContent(atest3);

}

/**
Expand Down Expand Up @@ -128,7 +130,29 @@ void copyTest() {
// references
assertEquals("rc1", atest2prime.refcont.test3); // should be pointing to above

// assertEquals("rc1" ,atest3.refBad.test3);//TODO not sure which way we want these to work.
// assertEquals("rc1" ,atest3.refBad.test3);//TODO not sure which way we want these to work - it is actually a failure of design

}

@Test
void deleteTest() {
jakarta.persistence.EntityManager em =
setupH2Db(SampleModel.pu_name()); // IMPL build means that everything is in one
// persistence unit.
em.getTransaction().begin();
model.management().persistRefs(em);
em.persist(atest2);
em.getTransaction().commit();
Long id = atest2.getId();

// flush any existing entities
em.clear();
em.getEntityManagerFactory().getCache().evictAll();
ATest2 atest2in = em.createNamedQuery("ATest2.findById", ATest2.class).setParameter("id", id).getSingleResult();
assertNotNull(atest2in);
em.getTransaction().begin();
atest2in.delete(em); //IMPL
em.getTransaction().commit();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;

import jakarta.persistence.EntityManager;

/*
* Created on 16/05/2023 by Paul Harrison ([email protected]).
*/
Expand Down Expand Up @@ -31,6 +33,13 @@ public BaseC copyMe() {
"Type1728055728713.copyMe() not implemented");

}

@Override
public void delete(EntityManager em) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("JPAManipulationsForObjectType<Long>.delete() not implemented");

}
}, "eval");
fail("should have thown exception for value outside vocab");
} catch (Exception e) {
Expand Down
18 changes: 4 additions & 14 deletions tools/xslt/common-structure-functions.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,8 @@ note - only define functions in here as it is included in the schematron rules
<xsl:param name="vodml-ref" as="xsd:string"/>
<xsl:choose>
<xsl:when test="$models/key('ellookup',$vodml-ref)">
<xsl:variable name="el" as="element()">
<xsl:copy-of select="$models/key('ellookup',$vodml-ref)" />
</xsl:variable>
<xsl:sequence select="$el/reference/datatype/vodml-ref"/>
<xsl:for-each select="$el/composition/datatype/vodml-ref">
<xsl:sequence select="for $t in ($models/key('ellookup',$vodml-ref),vf:baseTypes($vodml-ref)) return $t/reference/datatype/vodml-ref"/>
<xsl:for-each select="for $t in ($models/key('ellookup',$vodml-ref),vf:baseTypes($vodml-ref)) return $t/composition/datatype/vodml-ref">
<!-- <xsl:message><xsl:value-of select="concat('subtype of ',$vodml-ref, ' is ', name)" /></xsl:message>-->
<xsl:sequence select="vf:referenceTypesInContainmentHierarchy(.)"/>
</xsl:for-each>
Expand Down Expand Up @@ -544,15 +541,8 @@ note - only define functions in here as it is included in the schematron rules
<xsl:function name="vf:isTypeContainedBelow" as="xsd:boolean">
<xsl:param name="type-vodml-ref" as="xsd:string" />
<xsl:param name="root-vodml-ref" as="xsd:string"/>
<xsl:variable name="root" select="$models/key('ellookup',$root-vodml-ref)"/>
<xsl:choose>
<xsl:when test="$root/composition">
<xsl:sequence select="$type-vodml-ref = vf:containedTypes($root-vodml-ref)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="false()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:variable name="ct" select="vf:containedTypes($root-vodml-ref)"/>
<xsl:sequence select="$type-vodml-ref = $ct"/>
</xsl:function>


Expand Down
5 changes: 4 additions & 1 deletion tools/xslt/common.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@
<xsl:value-of select="concat(./ancestor::vo-dml:model/name,':',.)" />
</xsl:template>


<xsl:function name="vf:multiple" as="xsd:boolean">
<xsl:param name="p" as="element()"/>
<xsl:sequence select="xsd:int($p/multiplicity/maxOccurs) ne 1"/>
</xsl:function>

<xsl:template match="multiplicity" mode="tostring">
<xsl:variable name="lower">
Expand Down
39 changes: 39 additions & 0 deletions tools/xslt/jpa.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,46 @@
</xsl:template>
<xsl:template match="*" mode="jpaConfig"><!-- do nothing --></xsl:template>

<!-- template to do smart deletion in the case of contained references
TODO could also do something better in the case of bulk deletion.-->
<xsl:template match="objectType" mode="jpadeleter">
<xsl:variable name="vodml-ref" select="vf:asvodmlref(current())"/>
<xsl:if test="not(@abstract)">
/**
* {@inheritDoc}
*/
@Override
public void delete(jakarta.persistence.EntityManager em) {
<xsl:variable name="crefs" select="distinct-values(vf:containedReferencesInContainmentHierarchy($vodml-ref))"/>

<xsl:choose>
<xsl:when test="count($crefs)> 0">
//has contained references <xsl:value-of select="string-join($crefs,',')"/>
<xsl:variable name="by" select="distinct-values(for $i in $crefs return vf:referredBy($i))"/>
//referred to by <xsl:value-of select="concat(string-join($by,','),$nl)"/>
<!-- TODO this only deals with referrers same level of containment.... -->
<xsl:for-each select="for $v in (vf:baseTypes($vodml-ref),$models/key('ellookup',$vodml-ref)) return $v/(composition)[datatype/vodml-ref= $by]"> <!--assume JPA will deal with attributes OK... -->
<xsl:choose>
<xsl:when test="vf:multiple(current())">
<!-- IMPL perhaps we could do bulk delete to speed things up -->
<xsl:value-of select="vf:javaMemberName(current()/name)"/>.stream().forEach(i -> em.remove(i));
</xsl:when>
<xsl:otherwise>
em.remove(<xsl:value-of select="vf:javaMemberName(current()/name)"/>);
</xsl:otherwise>
</xsl:choose>

</xsl:for-each>
em.remove(this); // finish up with itself.
</xsl:when>
<xsl:otherwise>
em.remove(this); // nothing special to do
</xsl:otherwise>
</xsl:choose>

}
</xsl:if>
</xsl:template>


</xsl:stylesheet>
1 change: 1 addition & 0 deletions tools/xslt/vo-dml2java.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@

<xsl:apply-templates select="." mode="jpawalker"/>
<xsl:apply-templates select="." mode="jparefs"/>
<xsl:apply-templates select="./self::objectType" mode="jpadeleter"/>

<!-- <xsl:if test="local-name() eq 'dataType'">-->
<!-- <xsl:apply-templates select="." mode="JPAConverter"/>-->
Expand Down
2 changes: 1 addition & 1 deletion tools/xslt/vo-dml2md.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ classDiagram
<xsl:if test="constraint[ends-with(@xsi:type,':NaturalKey')]">
<xsl:value-of select="concat(' :material-key-variant:{title=',$dq,'natural key',$dq,'}')"/>
</xsl:if>
<xsl:if test="./ancestor-or-self::reference">
<xsl:if test="./self::reference">
<xsl:value-of select="concat(' :material-arrow-top-right:{title=',$dq,'reference',$dq,'}')"/>
</xsl:if>

Expand Down
7 changes: 6 additions & 1 deletion xsd/vo-dml-binding.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ targetNamespace="http://www.ivoa.net/xml/vodml-binding/v0.9.1" attributeFormDefa
<xsd:documentation>the list of type mappings for the model.</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="type-detail" type="TypeDetail" minOccurs="0" maxOccurs="1"></xsd:element>
<xsd:element name="type-detail" type="TypeDetail" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>the list of type detail the model.</xsd:documentation>
</xsd:annotation>

</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="xmlnsMapping">
Expand Down

0 comments on commit 98d8fb5

Please sign in to comment.