I'm using Hibernate annotations to execute a stored procedure in SQL server and I want to use @SqlResultSetMapping to have the results returned as a collection of entities.
What I'm trying to accomplish is have each member of the result set be a single instance of my Java class, with that classes properties that map to @OneToOne relationships populated by other Java objects hydrated from other columns returned by the stored procedure. I want to do this in one round-trip (i.e. I don't want Hibernate going back to SQL to fill in any missing properties).
My simplified schema (excuse the loose syntax) is very straightforward
Code:
TABLE A {
PRIMARY KEY BIGINT id
NVARCHAR(30) name
}
TABLE B {
PRIMARY KEY BIGINT id FOREIGN KEY REFERENCES (A,Id)
NVARCHAR(30) name
NVARCHAR(20) type
}
My Java mapping is
Code:
@Entity
class A {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
long id;
String name;
@OneToOne(mappedBy="a")
B b;
}
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="Type",discriminatorType=DiscriminatorType.STRING)
@GenericGenerator(name="JOIN_GENERATOR",strategy="foreign",parameters={@Parameter(name="property",value="a")})
abstract class B {
@Id
@GeneratedValue(generator="JOIN_GENERATOR")
long id;
String name;
@OneToOne
@PrimaryKeyJoinColumn
A a;
}
@Entity
@DiscriminatorValue("B1")
class B1 extends B { }
@Entity
@DiscriminatorValue("B2")
class B2 extends B { }
i.e. I have a Class A that has a One-to-one relationship to a class B, the latter of which is mapped to
a single-table inheritance hierarchy realised by two concrete types B1 and B2.
My SQL Query looks something like this
Code:
SELECT TA.id id, TA.name name, TB.id B_id, TB.type B_type, TB.name B_name
FROM A TA LEFT OUTER JOIN B TB ON TA.id = TB.id
i.e. there is enough information returned in the query for Hibernate to hydrate an instance of A with an appropriate reference to a hydrated instance of B1 or B2.
I've tried numerous @SqlResultSetMapping strategies without complete success.
The first was to declare each class as an entity e.g. something like the following (note that implicit column-property binding is used for A but is not used for B because otherwise there would be a clash as both tables have id and name columns).
Code:
@SqlResultSetMapping(
name="myMapping",
entities={
@EntityResult(entityClass=A.class),
@EntityResult(entityClass=B.class,
fields={
@FieldResult(name="id",column="B_id"),
@FieldResult(name="name",column="B_name")
},
@discriminatorColumn("B_type")
}
)
- but this returned each result as a tuple <A,B> rather than just A.
My second attempt was to attempt to remove the top-level presence of B from each result by not declaring it as an entity in its own right but referencing the properties by "." notation i.e.
Code:
entities={
@EntityResult(entityClass=A.class,
fields={
@FieldResult(name="b.id",column="B_id"),
@FieldResult(name="b.name",column="B_name")
}
}
)
However there are two problems with this: the first is that I've lost the ability to specify the discriminatorColumn for B as there is no @EntityResult to hang it off. I can't see how Hibernate is going to know how to rehydrate the correct subclass (B1 or B2) as it won't know which column in the result set represents the discriminator.
The second problem is more immediate; I get the following error running hbm2ddl on my annotated java classes:
Code:
[hibernatetool] org.hibernate.MappingException: dotted notation reference neither a component nor a many/one to one
[hibernatetool] java.lang.ClassCastException: org.hibernate.mapping.SimpleValue
Is what I'm attempting not possible? I can just about live with dealing with the mapping that returns tuples <A,B> as long as A's inner references to B are correct and Hibernate doesn't do any more roundtrips under the covers to populate it. But I wondered if there was a way of using @SqlResultSetMapping to only return properly hydrated instances of A with their b properties "eagerly fetched".