I have the following class structure with 2 base classes (Filter and Map).
@Entity
public abstract class Filter {
}
@Entity
public class AFilter extends Filter {
}
@Entity
public class BFilter extends Filter {
}
@Entity
public abstract class Map {
public abstract Filter getFilter();
}
@Entity
public class AMap extends Map {
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
private AFilter filter;
@Override
public AFilter getFilter() {
return filter;
}
public void setFilter(AFilter filter) {
this.filter = filter;
}
}
@Entity
public class BMap extends Map {
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
private BFilter filter;
@Override
public BFilter getFilter() {
return filter;
}
public void setFilter(BFilter filter) {
this.filter = filter;
}
}
When I query the database and the resultset has an instance of an AMap, I get the following exception:
@Override
public List<Map> getMaps() {
Criteria criteria = dao.getCurrentSession().createCriteria(Map.class);
return criteria.list();
}
org.hibernate.PropertyAccessException: Could not set field value [com.xxx.filter.BFilter@6197da84] value by reflection : [class com.xxx.map.AMap.filter] setter of com.xxx.map.AMap.filter
I tried to debug the Hibernate(v5.4.18) library a little bit, somehow it assumes Map.class's filter
property is an instance of BFilter, rather than a dynamic subclass based on the Map subclass type.
This is the query generated by Hibernate (edited to remove extra fields and join tables):
select this_.id as id2_103_4_, this_.name as name15_103_4_, this_.status as status16_103_4_, this_.filter_id as filter_25_103_4_, this_.DTYPE as dtype1_103_4_, claimfilte5_.id as id2_89_3_, claimfilte5_.companyId as companyi3_89_3_, claimfilte5_.tableName as tablenam5_89_3_, claimfilte5_.zoomLevel as zoomleve6_89_3_ from public.Map this_ left outer join public.Filter claimfilte5_ on this_.filter_id=claimfilte5_.id
Looking at the query, Hibernate does not select the dtype
column for the filter table. => Causing the problem.
This is the result of the query with psql:
id2_103_4_ | name15_103_4_ | status16_103_4_ | filter_25_103_4_ | dtype1_103_4_ | id2_89_3_ | companyi3_89_3_ | tablenam5_89_3_ | zoomleve6_89_3_
------------+---------------+-----------------+------------------+---------------+-----------+-----------------+-----------------+-----------------
245921700 | 123123 | t | 245921702 | BMap | 245921702 | 16 | B |
250077365 | Test2 | t | 250077367 | BMap | 250077367 | 4 | B |
250365744 | Test | t | 250365746 | BMap | 250365746 | 0 | B |
250367720 | test3 | f | 250367722 | BMap | 250367722 | 0 | B |
254371277 | gdal new test | t | 254371279 | BMap | 254371279 | 0 | B |
254371748 | test4 | t | 254371750 | AMap | 254371750 | 0 | A |
(6 rows)
When I add the dtype column manually (to show that the dtype column is set properly on the filter table):
id2_103_4_ | name15_103_4_ | status16_103_4_ | filter_25_103_4_ | dtype1_103_4_ | id2_89_3_ | companyi3_89_3_ | tablenam5_89_3_ | zoomleve6_89_3_ | dtype
------------+---------------+-----------------+------------------+---------------+-----------+-----------------+-----------------+-----------------+--------------
245921700 | 123123 | t | 245921702 | BMap | 245921702 | 16 | B | | BFilter
250077365 | Test1 | t | 250077367 | BMap | 250077367 | 4 | B | | BFilter
250365744 | Test | t | 250365746 | BMap | 250365746 | 0 | B | | BFilter
250367720 | test3 | f | 250367722 | BMap | 250367722 | 0 | B | | BFilter
254371277 | gdal new test | t | 254371279 | BMap | 254371279 | 0 | B | | BFilter
254371748 | test4 | t | 254371750 | AMap | 254371750 | 0 | A | | AFilter
(6 rows)
I can query the database without any problems if I create a criteria using subclasses:
dao.getCurrentSession().createCriteria(AMap.class)
or
dao.getCurrentSession().createCriteria(BMap.class)
but that's not what I want.
How can I get Hibernate to recognize the correct subclass?
Please also show the queries that are generated. I guess that your data might be messed up i.e. you have a AMap
that refers to a BFilter
rather than an AFilter
. Maybe you need to force the use of discriminators by annotating @DiscriminatorOptions(force = true)
on Filter
.
UPDATE:
The key point is that the fields have distinct names in the subtypes. Hibernate supports implicit downcasts i.e. it would be possible to use select m.filter from Map m
and that would resolve to the downcasted association. Since there are multiple possible downcasts that have that property, there is a conflict. I actually implemented support for this part in Hibernate, but I guess that the discriminator is simply missing in that special case.