Re: Question about optionality in one-to-one relationships

2024-01-11 Thread Riccardo De Menna
Hi all,

After looking into the source code I think that the method that determines if, 
at class generation time, a relationship is mandatory, is the following from 
org.apache.cayenne.map.DbRelationship:

@Override
public boolean isMandatory() {
for (DbJoin join : getJoins()) {
if (join.getSource().isMandatory()) {
return true;
}
}
return false;
}

It seems to me that there are a lot of cases where such a method would give a 
wrong result. Not only my one-to-one case but in general in any situation where 
the left side of a join is a meaningful value there could be other reasons for 
it being mandatory, and not just because we are trying to tell the relationship 
is mandatory. Not only, the most obvious missfire is on the toMany side of a 
one-to-many relationship where there would most likely be a PK on the left side 
of the join that is mandatory by design.

Wouldn’t it be more appropriate to have a flag in the modeler with which to 
mark relationships as mandatory for these specific cases?

Is there an easy workaround?

Regards,
Riccardo

> On 11 Jan 2024, at 00:02, Riccardo De Menna  wrote:
> 
> Hi Michael,
> 
> I’m aware of what ToDepPK does and the validation methods. I fact my problem 
> is that I have a whole bunch of code that forwards validation from vaadin to 
> cayenne back and forth and it depends on me telling it which attributes and 
> relationship are mandatory.
> 
> What I can’t seem to figure out is why this particular setup which is NOT a 
> mandatory relationship returns true to isMandatory() at class generation. 
> Because of this unexpected behaviour, my generated classes flag that 
> relationship as mandatory and trigger validation exceptions when they 
> shouldn’t.
> 
> I was hoping that someone was familiar with the inner workings of the class 
> generation templates and that I just have a typo somewhere.
> 
> Regards,
> Riccardo
> 
> 
>> On 10 Jan 2024, at 15:46, Michael Gentry  wrote:
>> 
>> Hi Riccardo,
>> 
>> There is also a validateForSave() that might be easier (handles inserts and
>> updates).
>> 
>> mrg
>> 
>> 
>> On Wed, Jan 10, 2024 at 8:34 AM Michael Gentry  wrote:
>> 
>>> Hi Riccardo,
>>> 
>>> The To Dep PK checkbox on the DbEntity isn't really intended to say "this
>>> relationship is required" (that is a by-product). It is designed to assign
>>> a PK from one entity to another.
>>> 
>>> Perhaps use a Pre-Persist Callback (ObjEntity tab) to verify the shipment
>>> has a shipment number?
>>> 
>>> 
>>> https://cayenne.apache.org/docs/4.1/cayenne-guide/#types-of-lifecycle-events
>>> 
>>> You could also use validateForInsert() I think, but you should also maybe
>>> do a validateForUpdate() if you go down that path.
>>> 
>>> mrg
>>> 
>>> 
>>> On Wed, Jan 10, 2024 at 6:50 AM Riccardo De Menna 
>>> wrote:
>>> 
 Hi Michael,
 
> Try deselecting the "To Dep PK" checkbox on the relationship
> in SHIPMENT_NUMBER -> SHIPMENT.
 
 I tryed both ToDep directions but they don’t seem to affect the result.
> 
> I can see how having that checked would make the relationship required
> because it ties SHIPMENT_NUMBER's PK to SHIPMENT's PK, therefore a
> SHIPMENT_NUMBER shouldn't exist without a corresponding SHIPMENT and it
> sounds like you want it to be separate.
 
 But I do expect the reverse relationship to be required… As I undrestood
 it, I would set ToDepPK on the SHIPMENT_NUMBER side and keep it off on the
 SHIPMENT side. I want to be able to generate numbers before having to link
 them to shipments but I also expect shipments non to exist without a 
 number.
 
 BTW, as I said to Nikita, if I forcefully ignore the flag and manually
 bypass my validation code depending on it, the ObjectContext does not seem
 to complain about it and does commit the save.
 
 I assume it’s just the flag being marked as mandatory for some other side
 condition but I still don’t know what to use in the template as a reliable
 replacement.
 
 Riccardo
 
> 
> mrg
> 
> 
> On Wed, Jan 10, 2024 at 12:10 AM Riccardo De Menna 
> wrote:
> 
>> Hi Michael,
>> 
>> Thank you for helping. Yes… you modeled exactly what I described down
 to
>> the delete rules. I imported your files in modeler and run class
 generation
>> but I still get the relationship (TO_SHIP in your model) to appear as
>> mandatory. Could it be an issue in class generation?
>> 
>> This is a little VE snippet I use in my template to output a static
 list
>> of mandatory relationships.
>> 
>> #foreach( $rel in ${object.DeclaredRelationships} )
>> #if (${rel.isMandatory()} && !${rel.ToMany} )
>>  #set($bar =
>> 
 $requiredRelationships.add("${stringUtils.capitalizedAsConstant($rel.Name)}_PROPERTY"))
>> #end
>> #end
>> public static final List REQUIRED_RELATIONSHIPS = List.of(
>> #foreac

Re: Question about optionality in one-to-one relationships

2024-01-11 Thread Riccardo De Menna
Hi Nikita,

I stumbled upon a discussion thread in which you advertised a pull request to 
allow the use of metadataUtils via cgen to access comments. Can you tell me how 
to use it? Works with modeler as well?

Thank you in advance,
Riccardo

> On 11 Jan 2024, at 09:22, Riccardo De Menna  wrote:
> 
> Hi all,
> 
> After looking into the source code I think that the method that determines 
> if, at class generation time, a relationship is mandatory, is the following 
> from org.apache.cayenne.map.DbRelationship:
> 
> @Override
> public boolean isMandatory() {
>for (DbJoin join : getJoins()) {
>if (join.getSource().isMandatory()) {
>return true;
>}
>}
>return false;
> }
> 
> It seems to me that there are a lot of cases where such a method would give a 
> wrong result. Not only my one-to-one case but in general in any situation 
> where the left side of a join is a meaningful value there could be other 
> reasons for it being mandatory, and not just because we are trying to tell 
> the relationship is mandatory. Not only, the most obvious missfire is on the 
> toMany side of a one-to-many relationship where there would most likely be a 
> PK on the left side of the join that is mandatory by design.
> 
> Wouldn’t it be more appropriate to have a flag in the modeler with which to 
> mark relationships as mandatory for these specific cases?
> 
> Is there an easy workaround?
> 
> Regards,
> Riccardo
> 
>> On 11 Jan 2024, at 00:02, Riccardo De Menna  wrote:
>> 
>> Hi Michael,
>> 
>> I’m aware of what ToDepPK does and the validation methods. I fact my problem 
>> is that I have a whole bunch of code that forwards validation from vaadin to 
>> cayenne back and forth and it depends on me telling it which attributes and 
>> relationship are mandatory.
>> 
>> What I can’t seem to figure out is why this particular setup which is NOT a 
>> mandatory relationship returns true to isMandatory() at class generation. 
>> Because of this unexpected behaviour, my generated classes flag that 
>> relationship as mandatory and trigger validation exceptions when they 
>> shouldn’t.
>> 
>> I was hoping that someone was familiar with the inner workings of the class 
>> generation templates and that I just have a typo somewhere.
>> 
>> Regards,
>> Riccardo
>> 
>> 
>>> On 10 Jan 2024, at 15:46, Michael Gentry  wrote:
>>> 
>>> Hi Riccardo,
>>> 
>>> There is also a validateForSave() that might be easier (handles inserts and
>>> updates).
>>> 
>>> mrg
>>> 
>>> 
>>> On Wed, Jan 10, 2024 at 8:34 AM Michael Gentry  wrote:
>>> 
 Hi Riccardo,
 
 The To Dep PK checkbox on the DbEntity isn't really intended to say "this
 relationship is required" (that is a by-product). It is designed to assign
 a PK from one entity to another.
 
 Perhaps use a Pre-Persist Callback (ObjEntity tab) to verify the shipment
 has a shipment number?
 
 
 https://cayenne.apache.org/docs/4.1/cayenne-guide/#types-of-lifecycle-events
 
 You could also use validateForInsert() I think, but you should also maybe
 do a validateForUpdate() if you go down that path.
 
 mrg
 
 
 On Wed, Jan 10, 2024 at 6:50 AM Riccardo De Menna 
 wrote:
 
> Hi Michael,
> 
>> Try deselecting the "To Dep PK" checkbox on the relationship
>> in SHIPMENT_NUMBER -> SHIPMENT.
> 
> I tryed both ToDep directions but they don’t seem to affect the result.
>> 
>> I can see how having that checked would make the relationship required
>> because it ties SHIPMENT_NUMBER's PK to SHIPMENT's PK, therefore a
>> SHIPMENT_NUMBER shouldn't exist without a corresponding SHIPMENT and it
>> sounds like you want it to be separate.
> 
> But I do expect the reverse relationship to be required… As I undrestood
> it, I would set ToDepPK on the SHIPMENT_NUMBER side and keep it off on the
> SHIPMENT side. I want to be able to generate numbers before having to link
> them to shipments but I also expect shipments non to exist without a 
> number.
> 
> BTW, as I said to Nikita, if I forcefully ignore the flag and manually
> bypass my validation code depending on it, the ObjectContext does not seem
> to complain about it and does commit the save.
> 
> I assume it’s just the flag being marked as mandatory for some other side
> condition but I still don’t know what to use in the template as a reliable
> replacement.
> 
> Riccardo
> 
>> 
>> mrg
>> 
>> 
>> On Wed, Jan 10, 2024 at 12:10 AM Riccardo De Menna 
>> wrote:
>> 
>>> Hi Michael,
>>> 
>>> Thank you for helping. Yes… you modeled exactly what I described down
> to
>>> the delete rules. I imported your files in modeler and run class
> generation
>>> but I still get the relationship (TO_SHIP in your model) to appear as
>>> mandatory. Could it be an issue in class generation?
>>> 
>>>

Re: Question about optionality in one-to-one relationships

2024-01-11 Thread Nikita Timofeev
Hi Riccardo,

I don't quite remember my advice there. In Modeler this metadata goes just
as a comment to entity/attribute/relationship. So you could set
something in the comment and get access to this information via
metadataUtils in the template (via
`metadataUtils.getComment(relationship)`).

Also I checked Cayenne logic and it turns out that Cayenne itself doesn't
use this `isMandatory()` method in ObjRelationship. It uses logic here [1],
in short it checks the combination of
`isSourceIndependentFromTargetChange()` flags and `isMandatory()` on
underlying db relationships. You could probably use something similar to it
in your template, though it could be a bit challenging to reproduce in full.

[1]
https://github.com/apache/cayenne/blob/master/cayenne/src/main/java/org/apache/cayenne/BaseDataObject.java#L565-L591


On Thu, Jan 11, 2024 at 4:17 PM Riccardo De Menna 
wrote:

> Hi Nikita,
>
> I stumbled upon a discussion thread in which you advertised a pull request
> to allow the use of metadataUtils via cgen to access comments. Can you tell
> me how to use it? Works with modeler as well?
>
> Thank you in advance,
> Riccardo
>
> > On 11 Jan 2024, at 09:22, Riccardo De Menna  wrote:
> >
> > Hi all,
> >
> > After looking into the source code I think that the method that
> determines if, at class generation time, a relationship is mandatory, is
> the following from org.apache.cayenne.map.DbRelationship:
> >
> > @Override
> > public boolean isMandatory() {
> >for (DbJoin join : getJoins()) {
> >if (join.getSource().isMandatory()) {
> >return true;
> >}
> >}
> >return false;
> > }
> >
> > It seems to me that there are a lot of cases where such a method would
> give a wrong result. Not only my one-to-one case but in general in any
> situation where the left side of a join is a meaningful value there could
> be other reasons for it being mandatory, and not just because we are trying
> to tell the relationship is mandatory. Not only, the most obvious missfire
> is on the toMany side of a one-to-many relationship where there would most
> likely be a PK on the left side of the join that is mandatory by design.
> >
> > Wouldn’t it be more appropriate to have a flag in the modeler with which
> to mark relationships as mandatory for these specific cases?
> >
> > Is there an easy workaround?
> >
> > Regards,
> > Riccardo
> >
> >> On 11 Jan 2024, at 00:02, Riccardo De Menna  wrote:
> >>
> >> Hi Michael,
> >>
> >> I’m aware of what ToDepPK does and the validation methods. I fact my
> problem is that I have a whole bunch of code that forwards validation from
> vaadin to cayenne back and forth and it depends on me telling it which
> attributes and relationship are mandatory.
> >>
> >> What I can’t seem to figure out is why this particular setup which is
> NOT a mandatory relationship returns true to isMandatory() at class
> generation. Because of this unexpected behaviour, my generated classes flag
> that relationship as mandatory and trigger validation exceptions when they
> shouldn’t.
> >>
> >> I was hoping that someone was familiar with the inner workings of the
> class generation templates and that I just have a typo somewhere.
> >>
> >> Regards,
> >> Riccardo
> >>
> >>
> >>> On 10 Jan 2024, at 15:46, Michael Gentry  wrote:
> >>>
> >>> Hi Riccardo,
> >>>
> >>> There is also a validateForSave() that might be easier (handles
> inserts and
> >>> updates).
> >>>
> >>> mrg
> >>>
> >>>
> >>> On Wed, Jan 10, 2024 at 8:34 AM Michael Gentry 
> wrote:
> >>>
>  Hi Riccardo,
> 
>  The To Dep PK checkbox on the DbEntity isn't really intended to say
> "this
>  relationship is required" (that is a by-product). It is designed to
> assign
>  a PK from one entity to another.
> 
>  Perhaps use a Pre-Persist Callback (ObjEntity tab) to verify the
> shipment
>  has a shipment number?
> 
> 
> 
> https://cayenne.apache.org/docs/4.1/cayenne-guide/#types-of-lifecycle-events
> 
>  You could also use validateForInsert() I think, but you should also
> maybe
>  do a validateForUpdate() if you go down that path.
> 
>  mrg
> 
> 
>  On Wed, Jan 10, 2024 at 6:50 AM Riccardo De Menna  >
>  wrote:
> 
> > Hi Michael,
> >
> >> Try deselecting the "To Dep PK" checkbox on the relationship
> >> in SHIPMENT_NUMBER -> SHIPMENT.
> >
> > I tryed both ToDep directions but they don’t seem to affect the
> result.
> >>
> >> I can see how having that checked would make the relationship
> required
> >> because it ties SHIPMENT_NUMBER's PK to SHIPMENT's PK, therefore a
> >> SHIPMENT_NUMBER shouldn't exist without a corresponding SHIPMENT
> and it
> >> sounds like you want it to be separate.
> >
> > But I do expect the reverse relationship to be required… As I
> undrestood
> > it, I would set ToDepPK on the SHIPMENT_NUMBER side and keep it off
> on the
> > SHIPMENT side. I want to be able 

Re: Question about optionality in one-to-one relationships

2024-01-11 Thread Riccardo De Menna
Hi Nikita,

Thank you very much for the pointers.
I will amend my templates to emulate cayenne and use the metadata comments for 
any custom behaviour. Thank you for that pull request.

Best regards,
Riccardo

> On 11 Jan 2024, at 17:36, Nikita Timofeev  wrote:
> 
> Hi Riccardo,
> 
> I don't quite remember my advice there. In Modeler this metadata goes just
> as a comment to entity/attribute/relationship. So you could set
> something in the comment and get access to this information via
> metadataUtils in the template (via
> `metadataUtils.getComment(relationship)`).
> 
> Also I checked Cayenne logic and it turns out that Cayenne itself doesn't
> use this `isMandatory()` method in ObjRelationship. It uses logic here [1],
> in short it checks the combination of
> `isSourceIndependentFromTargetChange()` flags and `isMandatory()` on
> underlying db relationships. You could probably use something similar to it
> in your template, though it could be a bit challenging to reproduce in full.
> 
> [1]
> https://github.com/apache/cayenne/blob/master/cayenne/src/main/java/org/apache/cayenne/BaseDataObject.java#L565-L591
> 
> 
> On Thu, Jan 11, 2024 at 4:17 PM Riccardo De Menna 
> wrote:
> 
>> Hi Nikita,
>> 
>> I stumbled upon a discussion thread in which you advertised a pull request
>> to allow the use of metadataUtils via cgen to access comments. Can you tell
>> me how to use it? Works with modeler as well?
>> 
>> Thank you in advance,
>> Riccardo
>> 
>>> On 11 Jan 2024, at 09:22, Riccardo De Menna  wrote:
>>> 
>>> Hi all,
>>> 
>>> After looking into the source code I think that the method that
>> determines if, at class generation time, a relationship is mandatory, is
>> the following from org.apache.cayenne.map.DbRelationship:
>>> 
>>> @Override
>>> public boolean isMandatory() {
>>>   for (DbJoin join : getJoins()) {
>>>   if (join.getSource().isMandatory()) {
>>>   return true;
>>>   }
>>>   }
>>>   return false;
>>> }
>>> 
>>> It seems to me that there are a lot of cases where such a method would
>> give a wrong result. Not only my one-to-one case but in general in any
>> situation where the left side of a join is a meaningful value there could
>> be other reasons for it being mandatory, and not just because we are trying
>> to tell the relationship is mandatory. Not only, the most obvious missfire
>> is on the toMany side of a one-to-many relationship where there would most
>> likely be a PK on the left side of the join that is mandatory by design.
>>> 
>>> Wouldn’t it be more appropriate to have a flag in the modeler with which
>> to mark relationships as mandatory for these specific cases?
>>> 
>>> Is there an easy workaround?
>>> 
>>> Regards,
>>> Riccardo
>>> 
 On 11 Jan 2024, at 00:02, Riccardo De Menna  wrote:
 
 Hi Michael,
 
 I’m aware of what ToDepPK does and the validation methods. I fact my
>> problem is that I have a whole bunch of code that forwards validation from
>> vaadin to cayenne back and forth and it depends on me telling it which
>> attributes and relationship are mandatory.
 
 What I can’t seem to figure out is why this particular setup which is
>> NOT a mandatory relationship returns true to isMandatory() at class
>> generation. Because of this unexpected behaviour, my generated classes flag
>> that relationship as mandatory and trigger validation exceptions when they
>> shouldn’t.
 
 I was hoping that someone was familiar with the inner workings of the
>> class generation templates and that I just have a typo somewhere.
 
 Regards,
 Riccardo
 
 
> On 10 Jan 2024, at 15:46, Michael Gentry  wrote:
> 
> Hi Riccardo,
> 
> There is also a validateForSave() that might be easier (handles
>> inserts and
> updates).
> 
> mrg
> 
> 
> On Wed, Jan 10, 2024 at 8:34 AM Michael Gentry 
>> wrote:
> 
>> Hi Riccardo,
>> 
>> The To Dep PK checkbox on the DbEntity isn't really intended to say
>> "this
>> relationship is required" (that is a by-product). It is designed to
>> assign
>> a PK from one entity to another.
>> 
>> Perhaps use a Pre-Persist Callback (ObjEntity tab) to verify the
>> shipment
>> has a shipment number?
>> 
>> 
>> 
>> https://cayenne.apache.org/docs/4.1/cayenne-guide/#types-of-lifecycle-events
>> 
>> You could also use validateForInsert() I think, but you should also
>> maybe
>> do a validateForUpdate() if you go down that path.
>> 
>> mrg
>> 
>> 
>> On Wed, Jan 10, 2024 at 6:50 AM Riccardo De Menna >> 
>> wrote:
>> 
>>> Hi Michael,
>>> 
 Try deselecting the "To Dep PK" checkbox on the relationship
 in SHIPMENT_NUMBER -> SHIPMENT.
>>> 
>>> I tryed both ToDep directions but they don’t seem to affect the
>> result.
 
 I can see how having that checked would make the relationship
>> required
 because it ties SHIPMENT_NUMBER's PK to