Mantis - Resin
Viewing Issue Advanced Details
3335 major always 02-11-09 08:25 09-11-09 15:42
rdhauwe  
ferg  
normal  
closed 4.0.0  
fixed  
none    
none 4.0.2  
0003335: ManyToOne mapping impossible if primary key defined in super class entity
I am using a class AbstractEntity defining the primary key of my entities:

@MappedSuperclass
public class AbstractEntity {

    private Integer id;

    @Id
    @Column(name = "ID")
    @GeneratedValue
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}

I don't define the primary key in entities A and B inheriting from AbstractEntity.

When defining a ManyToOne relationship from B to A, Resin gives the following error message at startup/deployment:

[17:21:10.156] {http--8080-15} B.getA: Number of @JoinColumns for 'a' (1) does
not match the number of primary key columns for 'A' (0).

The annotation is as follows on B:

        @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "A_ID")
    public A getA() {
        return a;
    }

The cause seems to be the fact that the primary key of A is defined/annotated in the superclass AbstractEntity, and not in the class A itself.
 C:\Java\workspace\aclvb\resin\src\com\caucho\amber\cfg\AbstractRelationConfig.java [^] (8,914 bytes) 02-12-09 05:32
 C:\Java\workspace\aclvb\resin\src\com\caucho\amber\field\Id.java [^] (12,241 bytes) 02-12-09 05:32

Notes
(0003823)
rdhauwe   
02-12-09 03:57   
Some debugging information from AbstractRelationConfig.validateJoinColumns(...)

[12:55:55.765] {main} EntityType[B] field ManyToOneField[a,EntityType[A]]
TargetType:SubEntityType[A]
TargetType Id:com.caucho.amber.field.Id@1cfd3b2 columns 0
ParentType:MappedSuperclassType[AbstractEntity]
ParentType Id:com.caucho.amber.field.Id@1cfd3b2 columns 0 (<<-- SHOULD BE 1)

(0003824)
rdhauwe   
02-12-09 04:17   
I have looked into the class com.caucho.amber.field.Id.

Is it possible that the following code contains a bug?

  /**
   * Returns all the keys.
   */
  public int getKeyCount()
  {
    return _columns.size();
  }

Shouldn't this be replaced by :

  /**
   * Returns all the keys.
   */
  public int getKeyCount()
  {
    return _keys.size();
  }
(0003825)
rdhauwe   
02-12-09 04:28   
Id contains the following code, this seems to indicate that _columns remains empty when keys are added to a mapped super class.

  /**
   * Adds a new field to the id.
   */
  protected void addKey(IdField key)
  {
    _keys.add(key);
    // ejb/0a04
    // Collections.sort(_keys, new AmberFieldCompare());

    // jpa/0ge2
    if (_ownerType instanceof MappedSuperclassType)
      return;

    // jpa/0gg0
    if (_ownerType.isAbstractClass())
      return;

    _columns.addAll(key.getColumns());
    // Collections.sort(_columns, new ColumnCompare());

    for (AmberColumn column : key.getColumns()) {
      _ownerType.getTable().addIdColumn(column);
    }
  }

I think the problem is caused by AbstractRelationConfig depending on Id.getColumns() - which is empty for a mapped super class - and doesn't use Id.getKeyCount() nor Id.getKeys().
(0003826)
rdhauwe   
02-12-09 05:30   
The bug is indeed fixed with the following code, I will upload these 2 files.

Id:

 /**
   * Returns all the keys.
   */
  public int getKeyCount()
  {
    return _keys.size();
  }

AbstractRelationConfig:

  void validateJoinColumns(AccessibleObject field,
                           String fieldName,
                           HashMap<String,JoinColumnConfig> joinColumnMap,
                           EntityType targetType)
    throws ConfigException
  {
    if (joinColumnMap == null)
      return;

    com.caucho.amber.field.Id id = targetType.getId();
    
    EntityType parentType = targetType;

    int idCols;

    // XXX: jpa/0l48
    while ((idCols = id.getKeyCount()) == 0) {
      parentType = parentType.getParentType();

      if (parentType == null)
        break;

      id = parentType.getId();
    }

    int size;
    Object joinColumnCfg[] = null;

    size = joinColumnMap.size();
    joinColumnCfg = joinColumnMap.values().toArray();
 
    if (idCols != size) {
      throw error(field, L.l("Number of @JoinColumns for '{1}' ({0}) does not match the number of primary key columns for '{3}' ({2}).",
                             "" + size,
                             fieldName,
                             idCols,
                             targetType.getName()));
    }

    for (int i = 0; i < size; i++) {
      String ref;

      ref = ((JoinColumnConfig) joinColumnCfg[i]).getReferencedColumnName();
 
      if (((ref == null) || ref.equals("")) && size > 1)
        throw error(field, L.l("referencedColumnName is required when more than one @JoinColumn is specified."));

      IdField key = findKey(id.getKeys(), ref);

      if (key == null)
        throw error(field, L.l("referencedColumnName '{0}' does not match any key column in '{1}'.",
                               ref, targetType.getName()));
    }
  }
  
  static IdField findKey(ArrayList<IdField> arrayList, String ref)
  {
    if (((ref == null) || ref.equals("")) && arrayList.size() == 1)
      return arrayList.get(0);

    for (IdField column : arrayList) {
      if (column.getName().equals(ref)) {
        return column;
      }
    }

    return null;
  }
(0004242)
ferg   
09-11-09 15:42   
jpa/1910