5

I'm using Single Table Inheritance in Doctrine2 to store OAuth credentials for multiple services. I'd like to use the service's id as the primary key; however, that's not unique across all services.

I've setup the database to use the discriminator column and the service's id as the primary key, but I can't find a way to make Doctrine use the discriminator column as the key (in addition to the discriminator column). I'm using docblock annotations, and if I add the discriminator column as an @Id field I get an error:

Duplicate definition of column...in a field or discriminator column mapping.

If I only define the field as the discriminator column, any overlapping service ids update all the matching rows.

Anyway to make this work, other than just using an autogenerated it value?

Tim Lytle
  • 17,549
  • 10
  • 60
  • 91

2 Answers2

2

You can't, the descriminator column can't be used as part of the primary key.

Why do you need STI for this use-case btw? You have to create one new class per open-id service you provide, sounds pretty annoying :-)

beberlei
  • 4,297
  • 1
  • 23
  • 25
  • It's not that bad, since the individual service classes provide access to the service, they all just extend a Mapped Superclass to persist the common OAuth data. That way I relate a `$client` to various services. So after laoding a `$client` calling `$client->twitter->user->show($id)` would get a twitter user's data using the OAuth credentials of that `$client` Entity. I'm sure there are other/better ways to do this, but it's working reasonable well for now. – Tim Lytle Sep 28 '10 at 16:30
  • 2
    I would love to be able to use the discriminator as part of the PK as well: I have a use case where a table holds translations for various entities, and my ideal PK would be (languageCode, discriminator, entityId). Meanwhile, an auto-increment PK + a unique key on these 3 fields will do. – BenMorel Feb 13 '12 at 09:56
1

For those who use Hibernate, you can (at least as of JPA 2.1). The following code perfeclty works in my environment (hibernate-entitymanager 4.3.6.Final):

@Entity
@Table(name = "CODIFICATIONS")
@IdClass(CodificationId.class)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = Codification.DISCRIMINATOR_COLUMN, discriminatorType = DiscriminatorType.INTEGER)
public abstract class Codification implements Serializable {

    public static final String DISCRIMINATOR_COLUMN = "TABLE_ID";

    private static final long serialVersionUID = 1L;

    @Column(name = "CODIFICATION_ID")
    protected Long codificationId;

    @Id
    @Column(name = DISCRIMINATOR_COLUMN, insertable = false, updatable = false)
    protected Long tableId;

    @Id
    @Column(name = "CODE_ID", insertable = false, updatable = false)
    protected Long codeId;

    @Column(name = "LONG_NAME")
    protected String longName;

    @Column(name = "SHORT_NAME")
    protected String shortName;

}

public class CodificationId implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long tableId;
    private Long codeId;

    public Long getTableId() {
        return tableId;
    }

    public void setTableId(Long tableId) {
        this.tableId = tableId;
    }

    public Long getCodeId() {
        return codeId;
    }

    public void setCodeId(Long codeId) {
        this.codeId = codeId;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((tableId == null) ? 0 : tableId.hashCode());
        result = prime * result + ((codeId == null) ? 0 : codeId.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CodificationId other = (CodificationId) obj;
        if (tableId == null) {
            if (other.tableId != null)
                return false;
        } else if (!tableId.equals(other.tableId))
            return false;
        if (codeId == null) {
            if (other.codeId != null)
                return false;
        } else if (!codeId.equals(other.codeId))
            return false;
        return true;
    }

}

@Entity
@DiscriminatorValue(Status.DISCRIMINATOR_VALUE)
public class Status extends Codification {

    public static final String DISCRIMINATOR_VALUE = "2";

    private static final long serialVersionUID = 1L;

}

Then I configure an association to Status with the following code:

@ManyToOne
@JoinColumnsOrFormulas({
        @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = Codification.DISCRIMINATOR_COLUMN, value = Status.DISCRIMINATOR_VALUE)),
        @JoinColumnOrFormula(column = @JoinColumn(name = "STATUS", referencedColumnName = "CODE_ID")) })
private Status status;
Jean-Christophe
  • 624
  • 5
  • 11
  • I'm pretty sure that's not PHP. – gronostaj Jul 29 '19 at 11:40
  • No it's not, but it's "for those who use Hibernate". This subject isn't PHP specific. – Jean-Christophe Jul 29 '19 at 13:25
  • 2
    Well, the question is tagged [php] and [doctrine-orm] and it starts with _'I'm using Single Table Inheritance in Doctrine2 [...]"_, so I can't see how this can possibly be helpful for people facing OP's problem. – gronostaj Jul 29 '19 at 14:06
  • I only added a comment because it's also tagged [orm] and [single-table-inheritance], which are more general tags. It might also show the way to Doctrine's developers, I guess, if they ever look for a solution to this problem. – Jean-Christophe Jul 29 '19 at 14:27