Mike Clark
Mike Clark

Reputation: 11959

Hibernate validating wrong entity foreign key

I am running into an issue when Hibernate's EntityManager is starting up and validating the mapping of entities. In the simplest terms I can draft, I have the following four classes:

@Entity
@Table(name="abc_rule")
public class Rule {
  @Id
  @Column(name="id")
  private Integer id;

  @ManyToOne
  private RuleType ruleType;
}

@Entity
@Table(name="abc_rule_type")
@IdClass(RuleTypeKey.class)
public class RuleType {
  @Id
  private String typeCode;

  @Id
  private String otherCode;

  @Column(name="descr")
  private String description;
}

public class RuleTypeKey {
  @Id
  @Column(name="type_cd")
  private String typeCode;

  @Id
  @Column(name="other_cd")
  private String otherCode;
}

@Entity
@Table(name="abc_rule_type")
public class RuleTypeSimple {

  @Id
  @Column(name="id")
  private Integer id;

  @Column(name="type_cd")
  private String typeCode;

  @Column(name="other_cd")
  private String otherCode;

  @Column(name="descr")
  private String description;
}

When Hibernate starts up, I get the following exception:

Foreign key (FK4AD4C4B924F958E2:abc_rule [ruleType_type_cd,ruleType_other_cd])) must have same number of columns as the referenced primary key (abc_rule_type [id])

If I temporarily rename the @Table annotation on RuleTypeSimple to be "abc_rule_type_123" then Hibernate validates and uses the mapping as expected. To me this looks like Hibernate is validating foreign key mappings based on table name rather than on the setup of the actual classes being referenced.

Is there something obvious here I am missing that will force Hibernate to validate based on class? Or to make it not even validate foreign keys and just trust that I did it right?

As a side note, I know that there are not good reasons for having the class mapped twice with two different identifier setups. In my case it is caused the merging of two different projects of code acting on the same db. One is used in a read-only fashion and so, can get away with this unusual mapping. Unfortunately both code bases have a significant amount of code wrapped around their version of the class and refactoring will be a large undertaking which I hope can be avoided for the moment.

Upvotes: 1

Views: 1427

Answers (1)

Frank Orellana
Frank Orellana

Reputation: 1898

Remember that Hibernate is an ORM tool an so, you should try to take advantage of that by following some OOP rules or best practices.

Using several columns as primary key shouldn't be done at all, and if you are completely forced to do it (like if someone is going to kill you if you don't or you have an ancient Relational database already created and outside your human limits) then you should use an @Embedable Id annotated with @EmbeddedId and also do NOT annotate every column with the @Id annotation.

Here's an EmbeddedId example: EmbeddedCompoundPrimaryKey Example

Meanwhile, I'm not sure of what is the model you intend to create (it would help if you post the diagram or tables script at least), but with this corrections it should not give you more errors:

@Entity
@Table(name="abc_rule")
public class Rule {
  @Id
  @Column(name="id")
  private Integer id;

  @ManyToOne
  private RuleType ruleType;
}

@Entity
@Table(name="abc_rule_type")
//@IdClass(RuleTypeKey.class) <--I don't think this is right
public class RuleType {
  @EmbeddedId //Add this
  private RuleTypeKey id; //And this
  /* Remove this
  @Id
  private String typeCode;

  @Id
  private String otherCode;*/

  @Column(name="descr")
  private String description;
}

@Embeddable //Add this
public class RuleTypeKey {
  //@Id <--remove this
  @Column(name="type_cd")
  private String typeCode;

  //@Id <--remove this
  @Column(name="other_cd")
  private String otherCode;
}

@Entity
//@Table(name="abc_rule_type") <--must be different table name
@Table(name="abc_rule_type_simple")
public class RuleTypeSimple {

  @Id
  @Column(name="id")
  private Integer id;

  @Column(name="type_cd")  //Are this two colums a reference to 
  private String typeCode; //The RuleType too?? if so the must be
                           //The same as in class Rule (ManyToOne)

  @Column(name="other_cd")
  private String otherCode;

  @Column(name="descr")
  private String description;
}

try it and let me know if it works (it might have a couple of sintax errors, check it)

Upvotes: 1

Related Questions