Our setup (same spring and hibernate versions as above):
Two abstract classes:
A and B
Code:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
@Table(name = "A")
public abstract class A implements Comparable<A> {
@Id
@Column(name = "ID")
@SequenceGenerator(name = "a_id_seq_name", sequenceName = "a_id_seq", initialValue = 1000)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "a_id_seq_name")
protected Long id;
and
Code:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
@Table(name="B")
public abstract class B implements Comparable<B>{
@Id@Column(name="ID")@SequenceGenerator(name = "b_id_seq_name", sequenceName = "b_id_seq", initialValue = 1000)@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="b_id_seq_name")
protected Long id;
}
A has two subclasses A1 and A2, So does B: B1 and B2. Both Table per class hierarchy
A1 has a one-to-one relationship with B1:
Code:
@Entity@DiscriminatorValue("A1")
public class A1 extends A{
@OneToOne(mappedBy = "a1", cascade = {CascadeType.ALL})
private B1 b1;
}
Code:
@Entity@DiscriminatorValue("B1")
public class B1 extends B{
@OneToOne(targetEntity = A1.class) @JoinColumn(name = "A_ID", nullable = false)
private A1 a1;
// add an attribute to get the same error as the user above
@Column(name = "attribute", nullable = false)
private Double attribute;
A2 has a one-to-many with B2:
Code:
@Entity@DiscriminatorValue("A2")
public class A2 extends A{
@OneToMany(mappedBy = "a2", cascade = {CascadeType.ALL})
@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<B2> b2s = new TreeSet<B2>();
Code:
@Entity@DiscriminatorValue("B2")
public class B2 extends B{
@ManyToOne(targetEntity = A2.class) @JoinColumn(name = "A_ID", nullable = false)
private A2 a2;
The key here is that both B1 and B2 are using the same column 'A_ID' to point to their relation with A.
When using hibernate to load an object of type B1, it does so correctly, but it loads the property a1 in its second pass as an object of type A2. When it tries to set the property a1 as an object of type A2 you get the PropertyAccessException: could not set a field value by reflection setter issue.
We found that out while debugging with log4j.logger.org.hibernate=TRACE and stepping through the hibernate code.
The temporary work-around is as you say to put the mappings of B in xml, that works correctly:
Code:
<class name="B" table="B">
<id name="id" column="ID" type="long">
<generator class="sequence">
<param name="sequence">b_id_seq</param>
</generator>
</id>
<discriminator column="TYPE" type="string" not-null="true" />
</class>
Code:
<subclass name="B1" extends="B" discriminator-value="B1">
<many-to-one
name="a1"
lazy="false"
column="A_ID"
class="A1"/>
<property name="attribute" type="java.lang.Double" column="attribute"/>
</subclass>
Code:
<subclass name="B2" extends="B" discriminator-value="B2">
<many-to-one
name="a2"
lazy="false"
column="A_ID"
class="A2"/>
</subclass>
This should be filed as a bug imho. Or are we missing something?
Thanx for your time,
Rob.