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 <deme...@tuorlo.net> 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 <deme...@tuorlo.net> 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 <deme...@tuorlo.net> 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 <blackn...@gmail.com> 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 <blackn...@gmail.com> > 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 <deme...@tuorlo.net > > > >>>> 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 < > deme...@tuorlo.net> > >>>>>> 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<String> REQUIRED_RELATIONSHIPS = List.of( > >>>>>>> #foreach( $rel in ${requiredRelationships} ) > >>>>>>> ${rel}#if(!$foreach.last),#end > >>>>>>> #end > >>>>>>> ); > >>>>>>> > >>>>>>> And contrary to what I would expect, I get this in my superclass: > >>>>>>> > >>>>>>> public static final List<String> REQUIRED_RELATIONSHIPS = List.of( > >>>>>>> TO_SHIP_PROPERTY > >>>>>>> ); > >>>>>>> > >>>>>>> Riccardo De Menna > >>>>>>> > >>>>>>> > >>>>>>>> On 10 Jan 2024, at 01:20, Michael Gentry <blackn...@gmail.com> > wrote: > >>>>>>>> > >>>>>>>> Hi Riccardo, > >>>>>>>> > >>>>>>>> I may have completely misunderstood your intention, but here is my > >>>>> first > >>>>>>>> cut for a model: > >>>>>>>> > >>>>>>>> cayenne-o2o.xml: > >>>>>>>> <?xml version="1.0" encoding="utf-8"?> > >>>>>>>> <domain xmlns="http://cayenne.apache.org/schema/10/domain" > >>>>>>>> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > >>>>>>>> xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain > >>>>>>>> https://cayenne.apache.org/schema/10/domain.xsd" > >>>>>>>> project-version="10"> > >>>>>>>> <map name="datamap"/> > >>>>>>>> </domain> > >>>>>>>> > >>>>>>>> datamap.map.xml: > >>>>>>>> <?xml version="1.0" encoding="utf-8"?> > >>>>>>>> <data-map xmlns="http://cayenne.apache.org/schema/10/modelMap" > >>>>>>>> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > >>>>>>>> xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap > >>>>>>>> https://cayenne.apache.org/schema/10/modelMap.xsd" > >>>>>>>> project-version="10"> > >>>>>>>> <property name="defaultLockType" value="1"/> > >>>>>>>> <property name="defaultPackage" value="org.test"/> > >>>>>>>> <db-entity name="SHIPMENT"> > >>>>>>>> <db-attribute name="ID" type="BIGINT" isPrimaryKey="true" > >>>>>>> isMandatory="true" > >>>>>>>> /> > >>>>>>>> </db-entity> > >>>>>>>> <db-entity name="SHIPMENT_NUMBER"> > >>>>>>>> <db-attribute name="ID" type="BIGINT" isPrimaryKey="true" > >>>>>>> isMandatory="true" > >>>>>>>>> > >>>>>>>> <info:property xmlns:info=" > http://cayenne.apache.org/schema/10/info" > >>>>>>> name= > >>>>>>>> "comment" value="This ID is the same as SHIPMENT's ID."/> > >>>>>>>> </db-attribute> > >>>>>>>> <db-attribute name="NUM" type="INTEGER"> > >>>>>>>> <info:property xmlns:info=" > http://cayenne.apache.org/schema/10/info" > >>>>>>> name= > >>>>>>>> "comment" value="This is the shipment number."/> > >>>>>>>> </db-attribute> > >>>>>>>> </db-entity> > >>>>>>>> <obj-entity name="Shipment" className="org.test.Shipment" > lock-type= > >>>>>>>> "optimistic" dbEntityName="SHIPMENT"/> > >>>>>>>> <obj-entity name="ShipmentNumber" > className="org.test.ShipmentNumber" > >>>>>>>> lock-type="optimistic" dbEntityName="SHIPMENT_NUMBER"> > >>>>>>>> <obj-attribute name="num" type="java.lang.Integer" > >>>>>>> db-attribute-path="NUM"/> > >>>>>>>> </obj-entity> > >>>>>>>> <db-relationship name="toShipNum" source="SHIPMENT" > >>>>>>> target="SHIPMENT_NUMBER" > >>>>>>>>> > >>>>>>>> <db-attribute-pair source="ID" target="ID"/> > >>>>>>>> </db-relationship> > >>>>>>>> <db-relationship name="toShip" source="SHIPMENT_NUMBER" > >>>>> target="SHIPMENT" > >>>>>>>> toDependentPK="true"> > >>>>>>>> <db-attribute-pair source="ID" target="ID"/> > >>>>>>>> </db-relationship> > >>>>>>>> <obj-relationship name="toShipNum" source="Shipment" > >>>>>>> target="ShipmentNumber" > >>>>>>>> deleteRule="Cascade" db-relationship-path="toShipNum"/> > >>>>>>>> <obj-relationship name="toShip" source="ShipmentNumber" > >>>>> target="Shipment" > >>>>>>>> deleteRule="Deny" db-relationship-path="toShip"/> > >>>>>>>> </data-map> > >>>>>>>> > >>>>>>>> Copy/Paste these files somewhere, then try loading them up into > your > >>>>> 4.2 > >>>>>>>> Modeler and see if it is close. > >>>>>>>> > >>>>>>>> mrg > >>>>>>>> > >>>>>>>> > >>>>>>>> On Tue, Jan 9, 2024 at 9:54 AM Riccardo De Menna < > deme...@tuorlo.net> > >>>>>>> wrote: > >>>>>>>> > >>>>>>>>> Hi, > >>>>>>>>> > >>>>>>>>> Can someone help me understand something? > >>>>>>>>> > >>>>>>>>> I’m trying to model a one-to-one relationship between two > entities > >>>>> but I > >>>>>>>>> can’t seem to get the relationship to be optional. > >>>>>>>>> > >>>>>>>>> In my specific case I need to model an entity representing > shipments > >>>>>>> with > >>>>>>>>> a postal service. Each shipment needs to have a number taken > from a > >>>>>>>>> range/group that is pre-assigned by the postal service. > >>>>>>>>> > >>>>>>>>> Thus I created a SHIPMENT_NUMBER entity with just an INTEGER > >>>>> attribute > >>>>>>> and > >>>>>>>>> then used that attribute to build the relationship with the > SHIPMENT > >>>>>>>>> entity. Possibly with “To dep PK” as well. > >>>>>>>>> > >>>>>>>>> I want the relationship to be optional so that I can generate > >>>>>>>>> SHIPMENT_NUMBER as many as I want and populate them with the > numbers > >>>>>>>>> assigned by the postal service and only later, when the real > >>>>> SHIPMENT is > >>>>>>>>> actually needed/created, link it with the number in a one-to-one > >>>>>>> fashion. > >>>>>>>>> > >>>>>>>>> I’m not sure why, but my class generated content always shows the > >>>>>>>>> relationship as mandatory. > >>>>>>>>> > >>>>>>>>> Coming from the WebObjects world, I'm used to a modeler that > >>>>> explicitly > >>>>>>>>> shows checkboxes for isMandatory on relationships like with the > >>>>>>> attributes. > >>>>>>>>> Here in Cayenne it seems that optionality is implicitly > determined > >>>>>>> based on > >>>>>>>>> the design. > >>>>>>>>> > >>>>>>>>> Have I misunderstood something? Is my design flawed? > >>>>>>>>> > >>>>>>>>> Any tip is appreciated. > >>>>>>>>> > >>>>>>>>> Regards, > >>>>>>>>> Riccardo > >>>>>>> > >>>>>>> > >>>>> > >>>>> > >> > > > > -- Best regards, Nikita Timofeev