Re: Cross data map relationships

Andrus Adamchik <[email protected]> Tue, 14 Oct 2025 17:33:28 -0400
Newsgroups gmane.comp.java.cayenne.user
Message-ID <[email protected]>
> I then save the first  data map and it throws a NullPointerException. =20=


Before save, try this to make sure entities can find each other across =
DataMaps:

EntityResolver namespace =3D new EntityResolver(List.of(mainMap, =
errorsMap));

> (I avoided creating data nodes and the project in this code to keep =
things simple and illustrate the NullPointerException)

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.

Andrus


> 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