Here is a very minimal test of what I am seeing.
Two data maps: Main and Errors.
I have an APP_USER DbTable in the Main data map and an APP_ERROR DbTable in the
Errors data map.
Then I have a to-many relationship and inverse to one between these two.
APP_USER <->> APP_ERROR
I then simply create the ObjEntity, ObjAttribute and ObjRelationship objects
programmatically.
I then save the first data map and it throws a NullPointerException.
Here is the isolated test:
(I avoided creating data nodes and the project in this code to keep things
simple and illustrate the NullPointerException)
package play.cay.utils.conversion;
import java.io.PrintWriter;
import java.sql.Types;
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;
public class DataMapTest {
public static void main(String[] args) {
// Create DataMaps
DataMap mainMap = new DataMap("Main");
DataMap errorsMap = new DataMap("Errors");
// -------------------- DB Entities --------------------
// APP_USER DbEntity
DbEntity appUserDb = new DbEntity("APP_USER");
DbAttribute appUserIdDbAttr = 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();
// APP_ERROR DbEntity
DbEntity appErrorDb = new DbEntity("APP_ERROR");
DbAttribute appErrorIdDbAttr = 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));
// Add DbEntities to respective maps
mainMap.addDbEntity(appUserDb);
errorsMap.addDbEntity(appErrorDb);
// -------------------- DB Relationships --------------------
// APP_ERROR -> APP_USER (to-one)
DbRelationship toUserRel = new DbRelationship("appUser");
toUserRel.setSourceEntity(appErrorDb);
toUserRel.setTargetEntityName(appUserDb);
toUserRel.setToMany(false);
toUserRel.addJoin(new DbJoin(toUserRel, "APP_USER_ID", "ID"));
appErrorDb.addRelationship(toUserRel);
// APP_USER ->> APP_ERROR (to-many)
DbRelationship errorsRel = new DbRelationship("errors");
errorsRel.setSourceEntity(appUserDb);
errorsRel.setTargetEntityName(appErrorDb);
errorsRel.setToMany(true);
errorsRel.addJoin(new DbJoin(errorsRel, "ID", "APP_USER_ID"));
appUserDb.addRelationship(errorsRel);
// -------------------- ObjEntities --------------------
// APP_USER ObjEntity
ObjEntity appUserObj = 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));
// APP_ERROR ObjEntity
ObjEntity appErrorObj = new ObjEntity("AppError");
appErrorObj.setDbEntity(appErrorDb);
appErrorObj.addAttribute(new ObjAttribute("id", "java.lang.Integer",
appErrorObj));
appErrorObj.addAttribute(new ObjAttribute("errorMessage",
"java.lang.String", appErrorObj));
// Add ObjEntities to respective maps
mainMap.addObjEntity(appUserObj);
errorsMap.addObjEntity(appErrorObj);
// -------------------- ObjRelationships --------------------
// AppError â AppUser (to-one)
ObjRelationship appUserRel = new ObjRelationship("appUser");
appUserRel.setSourceEntity(appErrorObj);
appUserRel.setTargetEntityName(appUserObj); // sets target name
appUserRel.setDbRelationshipPath("appUser");
appErrorObj.addRelationship(appUserRel);
// AppUser â AppError (to-many)
ObjRelationship errorsRelObj = new ObjRelationship("errors");
errorsRelObj.setSourceEntity(appUserObj);
errorsRelObj.setTargetEntityName(appErrorObj); // sets target name
errorsRelObj.setDbRelationshipPath("errors");
appUserObj.addRelationship(errorsRelObj);
PrintWriter stdout = new PrintWriter(System.out, true);
XMLEncoder encoder = new XMLEncoder(stdout, "\t", "11");
EmptyConfigurationNodeVisitor visitor = new
EmptyConfigurationNodeVisitor();
stdout.println(mainMap.getName());
stdout.println();
encoder.nested(mainMap, visitor);
stdout.println();
stdout.println();
stdout.println(errorsMap.getName());
stdout.println();
encoder.nested(errorsMap, visitor);
}
}
>
> On Oct 14, 2025, at 3:29 PM, Ricardo Parada <[email protected]> wrote:
>
> Hello,
>
> I wrote a converter to convert our eomodels from EOF to Cayenne-project.xml
> and data maps .map.xml
>
> So far so good. However, I seem to be running into a problem for cross data
> map relationships.
>
> I’m using 5.0-M1.
>
> When I’m creating the ObjRelationship I call:
>
> objRel.setTargetEntityName(targetObjEntity);
>
> If I then call objRel.getTargetEntityName() it returns the correct target
> entity name.
>
> However, if I call objRel.getTargetEntity() it returns null.
>
> 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.
>
> 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.
>
> Is this a bug in 5.0-M1 or am I missing something when creating my
> ObjRelationship programmatically.
>
> I could put all eomodels that have cross eomodel relationships into the same
> data map to avoid this problem.
>
> 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.
>
> Thank you all in advance.
> Ricardo Parada