views:

256

answers:

1

Hi all,

I have a situation where I am working with EJB3 and a legacy database. I have a situation where there is a many-to-many relationship between two tables A and B, defined through a third (link) table L.

The complication is that the link table has other fields in it other than the PK's of tables A and B. The columns are standard timestamp and user columns to record who generated the link. These two additional columns are preventing me from defining the many-many relationship using a join table annotation, as they are not nillable and so must be populated.

Does anyone know of a way around this limitation? I could define One-to-many relationships from the link table to each of the other tables in the relationship, but this is not very elegant.

Thanks,

+1  A: 

Yes, it is but you need to make it elegant. The following super-class can be used to define arbitrary many-to-many relationship as an entity:

@MappedSuperclass
public abstract class ModelBaseRelationship {

    @Embeddable
    public static class Id implements Serializable {

        public Long entityId1;
        public Long entityId2;

        @Column(name = "ENTITY1_ID")
        public Long getEntityId1() {
            return entityId1;
        }

        @Column(name = "ENTITY2_ID")
        public Long getEntityId2() {
            return entityId2;
        }

        public Id() {
        }

        public Id(Long entityId1, Long entityId2) {
            this.entityId1 = entityId1;
            this.entityId2 = entityId2;
        }

        @Override
        public boolean equals(Object other) {
            if (other == null)
                return false;
            if (this == other)
                return true;
            if (!(other instanceof Id))
                return false;
            final Id that = (Id) other;
            return new EqualsBuilder().append(this.entityId1, that.getEntityId1()).append(this.entityId1, that.getEntityId2()).isEquals();
        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder(11, 111).append(this.entityId1).append(this.entityId2).toHashCode();
        }

        protected void setEntityId1(Long theEntityId1) {
            entityId1 = theEntityId1;
        }

        protected void setEntityId2(Long theEntityId2) {
            entityId2 = theEntityId2;
        }
    }

    protected Id id = new Id();

    public ModelBaseRelationship() {
        super();
    }

    public ModelBaseRelationship(ModelBaseEntity entity1, ModelBaseEntity entity2) {
        this();
        this.id.entityId1 = entity1.getId();
        this.id.entityId2 = entity2.getId();
        setVersion(0);
    }

    @EmbeddedId
    public Id getId() {
        return id;
    }

    protected void setId(Id theId) {
        id = theId;
    }

}

The example of entity based on this super class (fragment):

@Entity(name = "myRealEntity")
@Table(name = "REAL_TABLE_NAME", uniqueConstraints = { @UniqueConstraint(columnNames = {
 "FIRST_FK_ID", "SECOND_FK_ID" }) })
@AttributeOverrides( {
@AttributeOverride(name = "entityId1", column = @Column(name = "FIRST_FK_ID")),
@AttributeOverride(name = "entityId2", column = @Column(name = "SECOND_FK_ID"))    
})
public class ModelBaseRelationshipReferenceImpl extends ModelBaseRelationship {

  private Entity1OfManyToManyRelationship entity1;
  private Entity2OfManyToManyRelationship entity2;
  ...
  @ManyToOne
  @JoinColumn(name = "FIRST_FK_ID", insertable = false, updatable = false)
  public Entity1OfManyToManyRelationship getEntity1OfManyToManyRelationship() {
    return entity1;
  }

  @ManyToOne
  @JoinColumn(name = "SECOND_FK_ID", insertable = false, updatable = false)
  public Entity2OfManyToManyRelationship getEntity2OfManyToManyRelationship () {
    return entity2;
  }
...
}
grigory
Hi Grigory, thanks for the reply, but I'm not sure I'm following you properly here. What does this actually give you? At the moment it works using @ManyToOne annotations on A and B, and OneToMany on L - I'd prefer to get rid of the need for L (as an Entity). I'm not really sure what your superclass here achieves.
Ant
it does the following: it defines the link table as a complete entity with keeping unique key, primary key structure of the link table. I am going to edit last example to add relationships to make it clear.
grigory
The superclass is really abstract class that lets you reuse a lot of code in case you have more than one case of link table as an entity... Sorry for not clarifying this earlier. You can easily fold it into single entity class if you don't need re-use.
grigory