BeanMetadata Has-A representation in default Editor Layouts

How does the default Editor Layout handle the Has-A relationship declared in metadatatemplate files?
We do not see them represented at all.

During development, do we ever need to regenerate the entire database so that metadata changes are properly represented? How do metadata changes, during development, effect the database, previous data entries and new entries with the newer structure. We are not using editions.

Short answer: data-object-editor can only edit DataObjects and not DataObjectTrees. But since DataObjectTrees are composed of DataObjects you still use data-object-editor(s) to achieve your goal.

The “Has-A” relationship represented in the MetadataTemplate equates to parent child relationships which are expressed as DataObjectTrees. DataObjectTrees are composed of DataObjects. You can use the data-object-editor to create/edit 1 DataObject at a time. And then you can keep those created/edited data objects in memory and create a DataObjectTree out of them. The UX design might differ depending on your use case.

Starting in 3.2 ( to be formally released March 31st) we support ListOfDataObjects FieldMetadata in the data-object-editor. ListOfDataObjects is also a form of “Has A” relationship. Using a ListOfDataObjects FieldMetadata has some similarities to MetadataTemplate parent/child relationships but they each have their pros and cons.

Let me know if you want more information.

1 Like

Thanks. I’ll need to stew on your answer for while.

Can you speak to the second part of the question that relates to how Metadata definition changes effects the database and how past and future data are dealt with (without any editions).

In general if you don’t change editions, you can’t make changes to the BeanMetadata that would invalidate existing DataObjects or cause an invalid schema. For example, you cannot just remove a FieldMetadata or change a FieldMetadata’s type. The latter isn’t even legal with editions bc it would make the column name or Document key ambiguous.

If for example, you were adding a new field, that would be legal, although you should not make it required or it will invalidate existing DataObjects (unless you’re fine with requiring users to set the new required field on the next update, assuming updates come from users and not feeds of data). And LEAF when you add that field will alter tables if using SQL to add any new columns needed. Also if you increase the maxLength on a StringFieldMetadata, for example, LEAF will generate the alter statement needed to increase the column size. Note that those things can only happen automatically if the db user you give when setting up your SQL service as DDL permissions. If not, you can turn the Hibernate show_sql config on locally while testing to see what the schema changes needed are, and send that to your DBA.

In general, you shouldn’t need to regenerate the entire database and do migration, even if you have an evolving schema so long as you avoid breaking changes, and, if needed, leverage editions.

Inheritance is not, to my mind, an HAS-A relationship. A person has a house, it does not inherit a house. Inheritance is an IS-A relationship.

How many ways are there to define HAS-A relationships? What are the PROS/CONS for each type?

I see the ListOfDataObjectsField. Is there a singleton version, which would also be a HAS-A relationship?

So what is the MetadataTemplate file used for, if not to define HAS-A relationships?

Did you choose to not have the default object editor use the MetadataTemplate information, even when there are HAS-A relationships defined? Why? What good is the constructed object if it does not link to the HAS objects? I assume that the default object editor is then useless for MetadataTemplate defined relationships.

It seems like the questions we need to ask are all based on how you implemented these things and how that implementation relates to the generated DB and interactions services, all with their own implementation choices.

Given that we do not yet understand your implementation choices, of course we are making breaking changes.

Inheritance is not, to my mind, an HAS-A relationship

Agreed. With LEAF, a “IS-A” relationship is achieved with the extends attribute on BeanMetadata. Here is a useful page if you are interested in learning more on how this works in LEAF.

For clarification in LEAF terminology parent/child is used to refer to the HAS-A relationship created by a MetadataTemplate. Sub type and super type are used to reference an IS-A relationship.

How many ways are there to define HAS-A relationships? What are the PROS/CONS for each type?

There are 3 ways to create a HAS-A relationship between DataObjects

  1. Parent / child relationship with a MetadataTemplate (represented as DataObjectTrees)

Pros:
Child DataObjects are queryable with the benefit of having parentId and rootId calculated on the DataObjects. So if there exists Person objects that [HAS-A] Car, then I can query for cars that belong a Person of a specific ID.

Cons:
There is no concept of ordering

  1. ListOfDataObjects BeanMetadata field

Pros:
Order is maintained when persisted. Neatly nests DataObjects into other DataObjects

Cons:
These DataObjects cannot be queried and can only be retrieved by querying the DataObject that owns the ListOfDataObjects.

  1. Using a String or SetOfStrings FieldMetadata to reference IDs of other DataObjects

Pros:
DataObjects exist on their own and be queried individually

Cons:
You can’t query based on rootId / parentId so you will the DataObject that contains the reference ID and then load that DataObject.

data-object-editor in regards to DataObjectTrees

We do have plans to make a DataObjectTree editor but we don’t have a deliver date for that yet. The current limitation is the data-object-editor can only edit 1 data object at a time. Where as a DataObjectTree consists of multiple DataObjects. Most likely can compose together multiple data-object-editors to achieve your use case. I’d be happy to talk through this with you if you want.

Regarding Breaking Changes

To clarify, generally while developing, we would suggest just wiping the database if and when you’re going to make breaking changes, but that’s definitely up to your team to decide when and how to do that. Keep in mind removing fields or changing field types is a breaking change. Once you’re in production, though, generally those types of changes should be avoided, which is more what Mark was suggesting.

1 Like

@caleb.m.mchenry , it would be really great to talk about some of this. I’m available via cell and skype anytime.

Posting summary of our conversation here for future readers.

Say I have Person data that I know I can model with a Class.

class Person {
    Car car;
    House house;
}

I can represent the same HAS-A relationship in LEAF.

<?xml version="1.0" encoding="UTF-8"?>
<template name="MyTemplate">
    <node type="Person">
        <node type="Car" />
        <node type="House" />
    </node>
</template>

To persist my Person in LEAF I will need to create DataObject instances for each type. Then I can construct a DataObjectTree. The tree structure should match the hierarchy defined in the MetadataTemplate. I can then use the addRootTrees method on a DataObjectService to persist my data.

Person, Car, and House can be queried individually with a DataObjectQuery and the DataObjectService load method or I can load the entire tree structure with loadTrees.

Concerning the DataObject editor, if I wanted to have an editor to create our DataObjectTree of Person, Car, and House, then I can have a DataObject editor instance for Person, another for Car, and one more for House. Then I would construct a DataObjectTree using the three DataObjects received from those editors. Using a JavaScript DataObjectService client I can persist them to my data service with dataObjectService.addRootTrees

2 Likes

Hi Caleb,
To follow up from Jonathan we are trying to recreate this scenario of creating data objects to then create an object tree by using Junit without the UI. Do you have any Junit examples of a test that would go through the process of creating data-objects with the object tree. Or a similar/better way to go about testing through a scenario like this?

  • Thanks in advance !

Here’s a sample test from the framework-services repo using DataObjectTrees.

    @Test
    public void testSerializeTree() throws JsonParseException, JsonMappingException, IOException {
        String rootType = RandomUtils.randString();
        String childType1 = RandomUtils.randString();
        String childType2 = RandomUtils.randString();
        String grandchildType1 = RandomUtils.randString();

        DataObjectTree tree = DataObjectTree.newBuilder()
                .withDataObject(DataObject.newBuilder()
                        .withType(rootType)
                        .withUnsafe(RandomUtils.randString(), RandomUtils.randInt())
                        .build())
                .addChildTree(DataObjectTree.newBuilder()
                        .withDataObject(DataObject.newBuilder()
                                .withType(childType1)
                                .withUnsafe(RandomUtils.randString(), RandomUtils.randBool())
                                .build())
                        .addChild(DataObject.newBuilder()
                                .withType(grandchildType1)
                                .withUnsafe(RandomUtils.randString(), RandomUtils.randString())
                                .build())
                        .build())
                .addChild(DataObject.newBuilder()
                        .withType(childType2)
                        .withUnsafe(RandomUtils.randString(), RandomUtils.randFloat())
                        .build())
                .build();

        String json = MAPPER_INSTANCE.writeValueAsString(tree);
        DataObjectTree deserialized = MAPPER_INSTANCE.readValue(json, DataObjectTree.class);

        assertEquals(tree, deserialized);
        assertEquals(tree.hashCode(), deserialized.hashCode());
    }

I removed some asserts, since I think you’re probably less interested in those. But that shows the basics of making trees in unit tests. Technically you really only need the types on those DataObjects, but you can set any other fields your tests need too.

Thanks Mark,
After building the tree do you know what method I would need to invoke on Junit to send this object Tree as parameter to be persisted with the data service?

I am thinking after deserializing I should be able to use the rest service to send off the json data if thats the best way.

The method for persisting DataObjectTrees is called addRootTrees on the DataObjectServiceRestClient. Here are the javadocs for the class

Hi Caleb,
I tried to persist from my Junit test using addRootTrees with the correct existing entities I have defined and I am getting the below message regarding “User having insufficient permission to access them”. I need to persist a full data tree using Junit which is what I am currently trying to do or potentially from using the initial upsert data from data files inside a directory if that is a smarter way to go about it although I would have generate the data file with the correct structure.

Is there a chance we can get on a call? Thanks for the help and kind regards.

Uncaught exception while performing DataObject ADD.

[Note that this could be due to the entities not existing, or the User having insufficient permission to access them.]