Dynamic mapping

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Dynamic mapping

Andrus Adamchik
TL;DR: Map relationships dynamically in a running app if you need to connect multiple reusable ORM modules.

A longer version:

I'd often mention at various presentations that Cayenne supports generic objects and you can create mapping in runtime. I didn't have many real-life examples to demonstrate the need until now. But recently I encountered a good use case that was solved by dynamic mapping - reusable ORM modules.

Say I have a reusable lib.jar containing a Cayenne project and some persistent code built around it. Now I want to use it in app.jar (or app.war if you are still on JavaEE). app.jar has its own Cayenne project, with entities that need to reference entities in lib.jar. You can't relate them in the Modeler, short of unpacking lib.jar and reassembling a new project from both projects (the level of effort with such approach would kill most of the benefits of reuse).

Consider that in runtime EntityResolver would contain entities from both lib and app, so all we need is to connect them. So instead of messing with XML files, we'd create a DataChannelFilter in app.jar with "init" method that builds all needed relationships on the fly. Now we can access these relationships via generic DataObject API (on the app.jar side you can optionally create regular type-safe getters and setters). I wrote a utility relationship builder that makes this code transparent:

Relationships.oneToOne("libEntity")
        .between(AppEntity.class, LibEntity.class)
        .toDepPK()
        .joined(AppEntity.ID_PK_COLUMN, LibEntity.ID_PK_COLUMN)
        .createReverse("appEntity")
        .exec(resolver);

Needless to say that all this happens in a running application, and is not limited to relationships. E.g. you can create flattened attributes instead.

The implications are pretty exciting - you can write fully self-contained libraries with Cayenne that can be easily extended (without unpacking) with more tables, and otherwise integrated in app DB schemas. Dynamic mapping was the last missing piece of a puzzle in a modular CMS design that I am working on right now. Ironically the feature was there in Cayenne since Day 1, waiting to get noticed.

Andrus

---------------
Andrus Adamchik
@andrus_a | @ApacheCayenne

Reply | Threaded
Open this post in threaded view
|

Re: Dynamic mapping

Eric Schneider-2
I'm glad somebody finally noticed it.  :-)

> On Jan 19, 2017, at 8:29 PM, Andrus Adamchik <[hidden email]> wrote:
>
> TL;DR: Map relationships dynamically in a running app if you need to connect multiple reusable ORM modules.
>
> A longer version:
>
> I'd often mention at various presentations that Cayenne supports generic objects and you can create mapping in runtime. I didn't have many real-life examples to demonstrate the need until now. But recently I encountered a good use case that was solved by dynamic mapping - reusable ORM modules.
>
> Say I have a reusable lib.jar containing a Cayenne project and some persistent code built around it. Now I want to use it in app.jar (or app.war if you are still on JavaEE). app.jar has its own Cayenne project, with entities that need to reference entities in lib.jar. You can't relate them in the Modeler, short of unpacking lib.jar and reassembling a new project from both projects (the level of effort with such approach would kill most of the benefits of reuse).
>
> Consider that in runtime EntityResolver would contain entities from both lib and app, so all we need is to connect them. So instead of messing with XML files, we'd create a DataChannelFilter in app.jar with "init" method that builds all needed relationships on the fly. Now we can access these relationships via generic DataObject API (on the app.jar side you can optionally create regular type-safe getters and setters). I wrote a utility relationship builder that makes this code transparent:
>
> Relationships.oneToOne("libEntity")
>        .between(AppEntity.class, LibEntity.class)
>        .toDepPK()
>        .joined(AppEntity.ID_PK_COLUMN, LibEntity.ID_PK_COLUMN)
>        .createReverse("appEntity")
>        .exec(resolver);
>
> Needless to say that all this happens in a running application, and is not limited to relationships. E.g. you can create flattened attributes instead.
>
> The implications are pretty exciting - you can write fully self-contained libraries with Cayenne that can be easily extended (without unpacking) with more tables, and otherwise integrated in app DB schemas. Dynamic mapping was the last missing piece of a puzzle in a modular CMS design that I am working on right now. Ironically the feature was there in Cayenne since Day 1, waiting to get noticed.
>
> Andrus
>
> ---------------
> Andrus Adamchik
> @andrus_a | @ApacheCayenne
>
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic mapping

Hugi Thordarson
In reply to this post by Andrus Adamchik
As someone who uses multiple Cayenne projects in separate jars a lot (I have a project for db documentation, another one for user management, another one for generic object tagging etc. etc.) this is quite awesome and I have oodles of use cases. Can you share the code for the Relationships utility class?

Allowing the Modeler to load and handle multiple models simultaneously would also be very nice, but that’s a whole another story…

- hugi


> On 20. jan. 2017, at 01:29, Andrus Adamchik <[hidden email]> wrote:
>
> TL;DR: Map relationships dynamically in a running app if you need to connect multiple reusable ORM modules.
>
> A longer version:
>
> I'd often mention at various presentations that Cayenne supports generic objects and you can create mapping in runtime. I didn't have many real-life examples to demonstrate the need until now. But recently I encountered a good use case that was solved by dynamic mapping - reusable ORM modules.
>
> Say I have a reusable lib.jar containing a Cayenne project and some persistent code built around it. Now I want to use it in app.jar (or app.war if you are still on JavaEE). app.jar has its own Cayenne project, with entities that need to reference entities in lib.jar. You can't relate them in the Modeler, short of unpacking lib.jar and reassembling a new project from both projects (the level of effort with such approach would kill most of the benefits of reuse).
>
> Consider that in runtime EntityResolver would contain entities from both lib and app, so all we need is to connect them. So instead of messing with XML files, we'd create a DataChannelFilter in app.jar with "init" method that builds all needed relationships on the fly. Now we can access these relationships via generic DataObject API (on the app.jar side you can optionally create regular type-safe getters and setters). I wrote a utility relationship builder that makes this code transparent:
>
> Relationships.oneToOne("libEntity")
>        .between(AppEntity.class, LibEntity.class)
>        .toDepPK()
>        .joined(AppEntity.ID_PK_COLUMN, LibEntity.ID_PK_COLUMN)
>        .createReverse("appEntity")
>        .exec(resolver);
>
> Needless to say that all this happens in a running application, and is not limited to relationships. E.g. you can create flattened attributes instead.
>
> The implications are pretty exciting - you can write fully self-contained libraries with Cayenne that can be easily extended (without unpacking) with more tables, and otherwise integrated in app DB schemas. Dynamic mapping was the last missing piece of a puzzle in a modular CMS design that I am working on right now. Ironically the feature was there in Cayenne since Day 1, waiting to get noticed.
>
> Andrus
>
> ---------------
> Andrus Adamchik
> @andrus_a | @ApacheCayenne
>

Reply | Threaded
Open this post in threaded view
|

Re: Dynamic mapping

Andrus Adamchik
Here it is:

https://gist.github.com/andrus/29616d6f20fc5094a4eb77114d751db0

Hides lots of verbosity in Cayenne mapping API. We may port it to Cayenne in the future releases if we are to pursue the topic of dynamic mapping.

Andrus

> On Jan 20, 2017, at 8:24 AM, Hugi Thordarson <[hidden email]> wrote:
>
> As someone who uses multiple Cayenne projects in separate jars a lot (I have a project for db documentation, another one for user management, another one for generic object tagging etc. etc.) this is quite awesome and I have oodles of use cases. Can you share the code for the Relationships utility class?
>
> Allowing the Modeler to load and handle multiple models simultaneously would also be very nice, but that’s a whole another story…
>
> - hugi
>
>
>> On 20. jan. 2017, at 01:29, Andrus Adamchik <[hidden email]> wrote:
>>
>> TL;DR: Map relationships dynamically in a running app if you need to connect multiple reusable ORM modules.
>>
>> A longer version:
>>
>> I'd often mention at various presentations that Cayenne supports generic objects and you can create mapping in runtime. I didn't have many real-life examples to demonstrate the need until now. But recently I encountered a good use case that was solved by dynamic mapping - reusable ORM modules.
>>
>> Say I have a reusable lib.jar containing a Cayenne project and some persistent code built around it. Now I want to use it in app.jar (or app.war if you are still on JavaEE). app.jar has its own Cayenne project, with entities that need to reference entities in lib.jar. You can't relate them in the Modeler, short of unpacking lib.jar and reassembling a new project from both projects (the level of effort with such approach would kill most of the benefits of reuse).
>>
>> Consider that in runtime EntityResolver would contain entities from both lib and app, so all we need is to connect them. So instead of messing with XML files, we'd create a DataChannelFilter in app.jar with "init" method that builds all needed relationships on the fly. Now we can access these relationships via generic DataObject API (on the app.jar side you can optionally create regular type-safe getters and setters). I wrote a utility relationship builder that makes this code transparent:
>>
>> Relationships.oneToOne("libEntity")
>>       .between(AppEntity.class, LibEntity.class)
>>       .toDepPK()
>>       .joined(AppEntity.ID_PK_COLUMN, LibEntity.ID_PK_COLUMN)
>>       .createReverse("appEntity")
>>       .exec(resolver);
>>
>> Needless to say that all this happens in a running application, and is not limited to relationships. E.g. you can create flattened attributes instead.
>>
>> The implications are pretty exciting - you can write fully self-contained libraries with Cayenne that can be easily extended (without unpacking) with more tables, and otherwise integrated in app DB schemas. Dynamic mapping was the last missing piece of a puzzle in a modular CMS design that I am working on right now. Ironically the feature was there in Cayenne since Day 1, waiting to get noticed.
>>
>> Andrus
>>
>> ---------------
>> Andrus Adamchik
>> @andrus_a | @ApacheCayenne
>>
>

Reply | Threaded
Open this post in threaded view
|

Re: Dynamic mapping

Hugi Thordarson
Thanks! Now I have something fun to work on during the weekend :)

- hugi


> On 20. jan. 2017, at 14:26, Andrus Adamchik <[hidden email]> wrote:
>
> Here it is:
>
> https://gist.github.com/andrus/29616d6f20fc5094a4eb77114d751db0
>
> Hides lots of verbosity in Cayenne mapping API. We may port it to Cayenne in the future releases if we are to pursue the topic of dynamic mapping.
>
> Andrus
>
>> On Jan 20, 2017, at 8:24 AM, Hugi Thordarson <[hidden email]> wrote:
>>
>> As someone who uses multiple Cayenne projects in separate jars a lot (I have a project for db documentation, another one for user management, another one for generic object tagging etc. etc.) this is quite awesome and I have oodles of use cases. Can you share the code for the Relationships utility class?
>>
>> Allowing the Modeler to load and handle multiple models simultaneously would also be very nice, but that’s a whole another story…
>>
>> - hugi
>>
>>
>>> On 20. jan. 2017, at 01:29, Andrus Adamchik <[hidden email]> wrote:
>>>
>>> TL;DR: Map relationships dynamically in a running app if you need to connect multiple reusable ORM modules.
>>>
>>> A longer version:
>>>
>>> I'd often mention at various presentations that Cayenne supports generic objects and you can create mapping in runtime. I didn't have many real-life examples to demonstrate the need until now. But recently I encountered a good use case that was solved by dynamic mapping - reusable ORM modules.
>>>
>>> Say I have a reusable lib.jar containing a Cayenne project and some persistent code built around it. Now I want to use it in app.jar (or app.war if you are still on JavaEE). app.jar has its own Cayenne project, with entities that need to reference entities in lib.jar. You can't relate them in the Modeler, short of unpacking lib.jar and reassembling a new project from both projects (the level of effort with such approach would kill most of the benefits of reuse).
>>>
>>> Consider that in runtime EntityResolver would contain entities from both lib and app, so all we need is to connect them. So instead of messing with XML files, we'd create a DataChannelFilter in app.jar with "init" method that builds all needed relationships on the fly. Now we can access these relationships via generic DataObject API (on the app.jar side you can optionally create regular type-safe getters and setters). I wrote a utility relationship builder that makes this code transparent:
>>>
>>> Relationships.oneToOne("libEntity")
>>>      .between(AppEntity.class, LibEntity.class)
>>>      .toDepPK()
>>>      .joined(AppEntity.ID_PK_COLUMN, LibEntity.ID_PK_COLUMN)
>>>      .createReverse("appEntity")
>>>      .exec(resolver);
>>>
>>> Needless to say that all this happens in a running application, and is not limited to relationships. E.g. you can create flattened attributes instead.
>>>
>>> The implications are pretty exciting - you can write fully self-contained libraries with Cayenne that can be easily extended (without unpacking) with more tables, and otherwise integrated in app DB schemas. Dynamic mapping was the last missing piece of a puzzle in a modular CMS design that I am working on right now. Ironically the feature was there in Cayenne since Day 1, waiting to get noticed.
>>>
>>> Andrus
>>>
>>> ---------------
>>> Andrus Adamchik
>>> @andrus_a | @ApacheCayenne
>>>
>>
>