3

Object-Relational Mapping (ORM) mechanisms are widely available and used in other technology stacks, usually involving code generation. But searching here for ORM doesn't yield too much. While governor limits add hard boundaries on heap, row counts, and query counts, there are still many cases (at least in the business domain that I work in) that would fit within those boundaries.

The platform's SObjects are already a good representation of the data fields, and they have to be exposed as so much of the UI tooling relies on them. It is the relationships between objects that are not well supported as the __r mechanism has limited capabilities.

The features of the ORM mechanism would be something like this:

  • Model an arbitrarily deep hierarchy
  • Be creatable in memory and then saveable to the database
  • Read lazily from the database as relationships are accessed in code
  • Be generated from SObject describe data
  • Have built-in select * as the default
  • A generated class per SObject that wraps and exposes the SObject and adds the relationship navigation (i.e. type safe)
  • Optional base class and optional extending classes and factory mechanism so behavior can be added
  • Convention over configuration, but some pragmatic stuff like being able to mark some fields (e.g. big text fields) as not queried and apply a query limit to some types
  • Basic bulkification

Does anything of this nature already exist in open source form? Or do people write their own?

PS

Based on the comments (thank-you for those), I've just double checked my understanding that the __r fields can't be used in a general way as parent and child references would be in a normal object model. This test illustrates the various things you can't do (the tests would fail if you could), which makes it necessary to generate code to model the parent and child references:

@IsTest
private class RelationshipTest {

    @IsTest
    static void queried() {

        Account aa = new Account(Name = 'A1');
        insert aa;
        Contact cc = new Contact(AccountId = aa.Id, LastName = 'C1');
        insert cc;

        Account[] accounts = [
            select Name, (select Name from Contacts)
            from Account
            where Name = 'A1'
        ];
        System.assertEquals(1, accounts.size());

        for (Account a : accounts) {

            System.assertEquals(1, a.contacts.size());

            // Can't add to collection
            a.contacts.add(new Contact(LastName = 'C2'));
            System.assertEquals(1, a.contacts.size());

            // Can't remove from collection
            a.contacts.clear();
            System.assertEquals(1, a.contacts.size());
        }
    }

    @IsTest
    static void notQueried() {

        Account a = new Account(Name = 'A2');

        // Can't add to collection
        a.contacts.add(new Contact(LastName = 'C3'));
        System.assertEquals(0, a.contacts.size());

        // Can set parent reference
        Contact c = new Contact(LastName = 'C4');
        c.Account = a;

        // But setting parent reference has no effect on child collection
        System.assertEquals(0, a.contacts.size());
    }
}
2
  • It strikes me that your point #3 simply doesn't fit with Salesforce's governor limits - It would be way too easy to trip over the query/row limits.
    – Phil W
    Apr 6, 2019 at 15:59
  • @PhilW I agree for the general case. But we have quite a lot of cases where only a few hundred records make up the graph of objects that we want to work with but those records might be from a dozen types and arranged in a fairly deep hierarchy.
    – Keith C
    Apr 6, 2019 at 17:54

2 Answers 2

4

There's at least one library, by FinancialForce, you can use. Also, some features you're asking for are actually native. Let's go down the list.

Model an arbitrarily deep hierarchy

Be creatable in memory and then saveable to the database

This is fflib's UOW (Unit of Work) code. You can use this to insert/update/delete etc hierarchical records (e.g. an account, contact, and opportunity), and the library can act in an atomic manner (all or none consistency).

Read lazily from the database as relationships are accessed in code

Unfortunately, governor limits don't allow this. At minimum, you'd need to warm up a cache. I've written something like that in the past, but it's usually project-dependent.

Be generated from SObject describe data

Have built-in select * as the default

Yes, fflib uses describes to implement field level and object level security, and can generate dynamic queries via a Query Factory.

A generated class per SObject that wraps and exposes the SObject and adds the relationship navigation (i.e. type safe)

Unnecessary, as Salesforce already has that built-in. Try the following code in execute anonymous:

User u = new Contact().Account.Owner;

You won't get an error as traversing null sObject elements don't result in an error (but, you do need to check the final result for null values).

Optional base class and optional extending classes and factory mechanism so behavior can be added

There are some factories, although I'm not familiar with all of their capabilities.

Convention over configuration, but some pragmatic stuff like being able to mark some fields (e.g. big text fields) as not queried and apply a query limit to some types

It's fairly limited in this regard, but since it's open source, one could conceivably add this.

Basic bulkification

This is mostly handled by UOW, it allows you to build object graphs in memory and operate on everything at once. Of course, developers still need to make due diligence and bulkify the code that calls these classes.

Overall, fflib isn't a bad place to start. Other libraries have existed over time, but they're not nearly as well known, as far as I can tell.

5
  • Thanks for these thoughts; I'll look at the FinancialForce in more depth. At first glance, not really Hibernate-style ORM? Also maybe I'm missing something fundamental here, but I am under the impression that the relationship fields can only be populated via a query, and 1) as you can't query more than 1 down and 5 up, and 2) as you can't set them at all, they are not usable for the relationships in the general case. Hence the need to add/generate code for those relationships.
    – Keith C
    Apr 6, 2019 at 18:05
  • You can directly set an __r field in Apex code. We use this when we want to spoof a relationship to a new object in memory.
    – Phil W
    Apr 6, 2019 at 18:17
  • @PhilW Yeah my bad. But I remember e.g. when data has been queried, the list you get in a parent to child __r field is immutable. Wasn't that the point of the old sfab library. Or do I remember that wrong?
    – Keith C
    Apr 6, 2019 at 18:28
  • @KeithC I would think that given the limits of Apex, an arbitrarily complicated tree capability would be arbitrarily complicated, and ultimately limited by governor limits, probably either SOQL Queries, CPU, or Heap. fflib is about as good as it gets, from what I've seen, but if you find a better one I'd love to see it.
    – sfdcfox
    Apr 6, 2019 at 19:09
  • You can set parents only. Not children.
    – Adrian Larson
    Apr 8, 2019 at 10:54
2

I've gone ahead and created a small part of such a solution. It is a Visualforce page that uses describe calls to work out the relationships, and then generates a set of SObject wrappers that have parent/child relationship fields and methods that make sure both the parent and child references are always updated. It also generates a test class.

The open source is available here https://claimvantage.github.io/relationship-model-generator/.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .