I'm upgrading an entity library to be compatible with Hibernate 6.x and Jakarta EE. I get the following exception on querying for a class that inherits from a joined table entity. (Class / package names replaced for reasons):
class com.example.ServiceOrder cannot be cast to class com.example.GoodsOrder (com.example.ServiceOrder and com.example.GoodsOrder are in unnamed module of loader 'app')
Sample SQL test:
factory = Persistence.createEntityManagerFactory("order-persistence-unit");
entityManager = factory.createEntityManager();
final var query = entityManager.createQuery("select serviceOrder from com.example.ServiceOrder serviceOrder where serviceOrder.orderId = :orderId", ServiceOrder.class).setParameter("orderId", 1L);
final var serviceOrder = query.getSingleResult();
The classes inherit through a common super class, Order, and mapped via Join column annotation:
Sub Class
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table;
@Entity
@Table(name = "SERVICE_ORDER")
@PrimaryKeyJoinColumn(name = "SERVICE_ORDER_ID", referencedColumnName = "ORDER_ID")
public class ServiceOrder extends Order implements Serializable {
private Order order;
@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
public Order getOrder() { return this.order; }
.....
}
Super Class
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
@Entity
@Table( name = "Order" )
@Inheritance(strategy = InheritanceType.JOINED )
public class Order implements Serializable {
private Long orderId;
private GoodsOrder goodsOrder;
private ServiceOrder serviceOrder;
@GenericGenerator(
name = "com.example.OrderIdGenerator",
strategy = "org.hibernate.id.IdentityGenerator")
@Id
@GeneratedValue(generator = "com.example.OrderIdGenerator")
@Column( name = "ORDER_ID", unique = true, nullable = false)
public Long getOrderId() { return this.orderId; }
@OneToOne(fetch = FetchType.LAZY, mappedBy = "order")
public ServiceOrder getServiceOrder() { return this.serviceOrder; }
....
}
It correctly throws an exception when attempting to assign a field of type GoodsOrder with an instance of ServiceOrder.
This works as expected with Hibernate 5.x and javax classes. I haven't been able to find any references in the Hibernate 6 release that would give a clue why it's attribute mappings are incorrect based on annotations.
----Update----
After further testing in a simplified project, the error occurs when a super class has field references to more than 1 sub class. In the above example, having ServiceOrder and GoodsOrder fields in the Order class.
After further testing in a simplified project, the error occurs when a super class has field references to more than 1 sub class. In the above example, having ServiceOrder and GoodsOrder fields in the Order class.
@Entity
@Table( name = "Order" )
@Inheritance(strategy = InheritanceType.JOINED )
public class Order implements Serializable {
private Long orderId;
private GoodsOrder goodsOrder;
private ServiceOrder serviceOrder;
@GenericGenerator(name = "com.example.OrderIdGenerator",
strategy = "org.hibernate.id.IdentityGenerator")
@Id
@GeneratedValue(generator = "com.example.OrderIdGenerator")
@Column( name = "ORDER_ID", unique = true, nullable = false)
public Long getOrderId() { return this.orderId; }
@OneToOne(fetch = FetchType.LAZY, mappedBy = "order")
public ServiceOrder getServiceOrder() { return this.serviceOrder; }
@OneToOne(fetch = FetchType.LAZY, mappedBy = "order")
public GoodsOrder getGoodsOrder() { return this.goodsOrder; }
A little more exploratory testing showed that the mappedBy
property in @oneToOne
was causing Hibernate 6 to attempt to assign the instance queried for (ServiceOrder
) to other fields with that same mappedBy target (getGoodsOrder
) in the super class.
This library was generated by hibernate-tools reverse engineering an old database so the reasoning for using the same mappedBy
value is undocumented.
The current solution seems to be removing the propertymappedBy
on the super class fields for the sub classes.