Re: Cross data map relationships

Andrus Adamchik <[email protected]> Tue, 14 Oct 2025 17:39:19 -0400
Newsgroups gmane.comp.java.cayenne.user
Message-ID <[email protected]>
> EntityResolver namespace =3D new EntityResolver(List.of(mainMap, =
errorsMap));

A small correction. Actually this should look a bit different for it to =
have effect:

EntityResolver namespace =3D new EntityResolver();
namespace.addDataMap(mainMap);
namespace.addDataMap(errorsMap);

(Sigh, I hate mutable objects and side effects =F0=9F=99=82 In Cayenne =
6, we should have constructor/builder-only initialization of all configs =
and no set/add stuff).

Andrus


> On Oct 14, 2025, at 5:33=E2=80=AFPM, Andrus Adamchik =
<[email protected]> wrote:
>=20
>> I then save the first  data map and it throws a NullPointerException. =
=20
>=20
> Before save, try this to make sure entities can find each other across =
DataMaps:
>=20
> EntityResolver namespace =3D new EntityResolver(List.of(mainMap, =
errorsMap));
>=20
>> (I avoided creating data nodes and the project in this code to keep =
things simple and illustrate the NullPointerException)
>=20
> BTW, if you have a single DataNode, don't bother creating it in the =
model. It is much simpler and cleaner to just assign a DataSource when =
creating a CayenneRuntime, and Cayenne will use it to handle all =
DataMaps in the project.
>=20
> Andrus
>=20
>=20
>> On Oct 14, 2025, at 5:23=E2=80=AFPM, Ricardo Parada =
<[email protected]> wrote:
>>=20
>> Here is a very minimal test of what I am seeing.
>>=20
>> Two data maps: Main and Errors.
>>=20
>> I have an APP_USER DbTable in the Main data map and an APP_ERROR =
DbTable in the Errors data map.
>>=20
>> Then I have a to-many relationship and inverse to one between these =
two.
>>=20
>> APP_USER <->> APP_ERROR
>>=20
>> I then simply create the ObjEntity, ObjAttribute and ObjRelationship =
objects programmatically.
>>=20
>> I then save the first  data map and it throws a NullPointerException. =
=20
>>=20
>> Here is the isolated test:
>>=20
>> (I avoided creating data nodes and the project in this code to keep =
things simple and illustrate the NullPointerException)
>>=20
>> package play.cay.utils.conversion;
>>=20
>> import java.io.PrintWriter;
>> import java.sql.Types;
>>=20
>> import =
org.apache.cayenne.configuration.EmptyConfigurationNodeVisitor;
>> import org.apache.cayenne.map.DataMap;
>> import org.apache.cayenne.map.DbAttribute;
>> import org.apache.cayenne.map.DbEntity;
>> import org.apache.cayenne.map.DbJoin;
>> import org.apache.cayenne.map.DbRelationship;
>> import org.apache.cayenne.map.ObjAttribute;
>> import org.apache.cayenne.map.ObjEntity;
>> import org.apache.cayenne.map.ObjRelationship;
>> import org.apache.cayenne.util.XMLEncoder;
>>=20
>> public class DataMapTest {
>>=20
>>   public static void main(String[] args) {
>>       // Create DataMaps
>>       DataMap mainMap =3D new DataMap("Main");
>>       DataMap errorsMap =3D new DataMap("Errors");
>>=20
>>       // -------------------- DB Entities --------------------
>>=20
>>       // APP_USER DbEntity
>>       DbEntity appUserDb =3D new DbEntity("APP_USER");
>>       DbAttribute appUserIdDbAttr =3D new DbAttribute("ID", =
Types.INTEGER, appUserDb);
>>       appUserIdDbAttr.setPrimaryKey(true);
>>       appUserDb.addAttribute(appUserIdDbAttr);
>>       appUserDb.addAttribute(new DbAttribute("NAME", Types.VARCHAR, =
appUserDb));
>>       appUserDb.addAttribute(new DbAttribute("EMAIL", Types.VARCHAR, =
appUserDb));
>>       appUserDb.getPrimaryKeyGenerator();
>>=20
>>       // APP_ERROR DbEntity
>>       DbEntity appErrorDb =3D new DbEntity("APP_ERROR");
>>       DbAttribute appErrorIdDbAttr =3D new DbAttribute("ID", =
Types.INTEGER, appErrorDb);
>>       appErrorDb.addAttribute(appErrorIdDbAttr);
>>       appErrorDb.addAttribute(new DbAttribute("APP_USER_ID", =
Types.INTEGER, appErrorDb));
>>       appErrorDb.addAttribute(new DbAttribute("ERROR_MESSAGE", =
Types.VARCHAR, appErrorDb));
>>=20
>>       // Add DbEntities to respective maps
>>       mainMap.addDbEntity(appUserDb);
>>       errorsMap.addDbEntity(appErrorDb);
>>=20
>>       // -------------------- DB Relationships --------------------
>>=20
>>       // APP_ERROR -> APP_USER (to-one)
>>       DbRelationship toUserRel =3D new DbRelationship("appUser");
>>       toUserRel.setSourceEntity(appErrorDb);
>>       toUserRel.setTargetEntityName(appUserDb);
>>       toUserRel.setToMany(false);
>>       toUserRel.addJoin(new DbJoin(toUserRel, "APP_USER_ID", "ID"));
>>       appErrorDb.addRelationship(toUserRel);
>>=20
>>       // APP_USER ->> APP_ERROR (to-many)
>>       DbRelationship errorsRel =3D new DbRelationship("errors");
>>       errorsRel.setSourceEntity(appUserDb);
>>       errorsRel.setTargetEntityName(appErrorDb);
>>       errorsRel.setToMany(true);
>>       errorsRel.addJoin(new DbJoin(errorsRel, "ID", "APP_USER_ID"));
>>       appUserDb.addRelationship(errorsRel);
>>=20
>>       // -------------------- ObjEntities --------------------
>>=20
>>       // APP_USER ObjEntity
>>       ObjEntity appUserObj =3D new ObjEntity("AppUser");
>>       appUserObj.setDbEntity(appUserDb);
>>       appUserObj.addAttribute(new ObjAttribute("id", =
"java.lang.Integer", appUserObj));
>>       appUserObj.addAttribute(new ObjAttribute("name", =
"java.lang.String", appUserObj));
>>       appUserObj.addAttribute(new ObjAttribute("email", =
"java.lang.String", appUserObj));
>>=20
>>       // APP_ERROR ObjEntity
>>       ObjEntity appErrorObj =3D new ObjEntity("AppError");
>>       appErrorObj.setDbEntity(appErrorDb);
>>       appErrorObj.addAttribute(new ObjAttribute("id", =
"java.lang.Integer", appErrorObj));
>>       appErrorObj.addAttribute(new ObjAttribute("errorMessage", =
"java.lang.String", appErrorObj));
>>=20
>>       // Add ObjEntities to respective maps
>>       mainMap.addObjEntity(appUserObj);
>>       errorsMap.addObjEntity(appErrorObj);
>>=20
>>       // -------------------- ObjRelationships --------------------
>>=20
>>       // AppError =C3=A2=C2=86=C2=92 AppUser (to-one)
>>       ObjRelationship appUserRel =3D new ObjRelationship("appUser");
>>       appUserRel.setSourceEntity(appErrorObj);
>>       appUserRel.setTargetEntityName(appUserObj); // sets target name
>>       appUserRel.setDbRelationshipPath("appUser");
>>       appErrorObj.addRelationship(appUserRel);
>>=20
>>       // AppUser =C3=A2=C2=86=C2=92 AppError (to-many)
>>       ObjRelationship errorsRelObj =3D new ObjRelationship("errors");
>>       errorsRelObj.setSourceEntity(appUserObj);
>>       errorsRelObj.setTargetEntityName(appErrorObj); // sets target =
name
>>       errorsRelObj.setDbRelationshipPath("errors");
>>       appUserObj.addRelationship(errorsRelObj);
>>=20
>>       PrintWriter stdout =3D new PrintWriter(System.out, true);
>>       XMLEncoder encoder =3D new XMLEncoder(stdout, "\t", "11");
>>       EmptyConfigurationNodeVisitor visitor =3D new =
EmptyConfigurationNodeVisitor();
>>=20
>>       stdout.println(mainMap.getName());
>>       stdout.println();
>>       encoder.nested(mainMap, visitor);
>>=20
>>       stdout.println();
>>       stdout.println();
>>=20
>>       stdout.println(errorsMap.getName());
>>       stdout.println();
>>       encoder.nested(errorsMap, visitor);
>>=20
>>   }
>> }
>>=20
>>=20
>>=20
>>>=20
>>> On Oct 14, 2025, at 3:29=E2=80=AFPM, Ricardo Parada =
<[email protected]> wrote:
>>>=20
>>> =EF=BB=BFHello,
>>>=20
>>> I wrote a converter to convert our eomodels from EOF to =
Cayenne-project.xml and data maps .map.xml
>>>=20
>>> So far so good. However, I seem to be running into a problem for =
cross data map relationships.
>>>=20
>>> I=E2=80=99m using 5.0-M1.
>>>=20
>>> When I=E2=80=99m creating the ObjRelationship I call:
>>>=20
>>> objRel.setTargetEntityName(targetObjEntity);
>>>=20
>>> If I then call objRel.getTargetEntityName() it returns the correct =
target entity name. =20
>>>=20
>>> However, if I call objRel.getTargetEntity() it returns null.
>>>=20
>>> I debugged the code and I can see that getTargetEntity() is looking =
for the target entity name in the data map corresponding to the source =
ObjEntity. And it does not find it and returns null for that reason.
>>>=20
>>> As a result of that, when I call encodeAsXML() on objRel I see that =
the XML is missing the target attribute in the obj-relationship xml tag.
>>>=20
>>> Is this a bug in 5.0-M1 or am I missing something when creating my =
ObjRelationship programmatically.
>>>=20
>>> I could put all eomodels that have cross eomodel relationships into =
the same data map to avoid this problem.
>>>=20
>>> But when I create it in Cayenne Modeler 4.3.3 it seems to work. I =
see the obj-relationship in the XML have target correctly set.
>>>=20
>>> Thank you all in advance.
>>> Ricardo Parada
>=20