Mapped subclass is retrieved as superclass by Hibernate - java

I have a class Project, a superclass Resource with two subclasses StorageResource and ComputeResource. I have defined a Hibernate mapping (I am using Hibernate version 5.3.7.Final) as follows:
#Entity
#Table(name = "PROJECTS", ...})
public class Project implements Serializable {
...
#OneToMany(
mappedBy = "project",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Resource> resources;
...
}
#Entity
#Table(name = "resources")
#Inheritance(strategy = InheritanceType.JOINED)
public class Resource implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#ManyToOne
private Project project;
...
}
#Entity
#Table(name = "storage_resources")
#PrimaryKeyJoinColumn(name = "id")
public class StorageResource extends Resource implements Serializable {
...
}
#Entity
#Table(name = "compute_resources")
#PrimaryKeyJoinColumn(name = "id")
public class ComputeResource extends Resource implements Serializable {
...
}
My Java code can save instances of both subclasses (StorageResource and ComputeResource) correctly to the database. However, when I load these records by loading a project and its resources, I have the following:
Storage resource records are loaded as Java objects that are instances of StorageResource,
Compute resource records are loaded as Java objects that are instances of Resource, not of ComputeResource as I would expect.
I have spent the last two days trying to figure out why this is happening: the mappings of both sub-classes are the same. Am I missing something?
EDIT
I found the problem. It was quite misleading, because there was no error message. The environment in which the bug occurred had an outdated configuration file. The Hibernate mapping for ComputeResource
<mapping class="....ComputeResource"/>
was missing. After adding this line the mapping is working as expected.

Related

#OneToOne or #OneToMany references an unkown entity

I'm having some issues with an error that occurs from time to time on my project. I have some JPA entities in project 1 that are used on project 2.
I'm using micronaut (groovy), liquibase and hibernate.
The error I get is as follows:
2022-03-07 09:44:18.513 xxxxx-xxx-xxx ERROR [xxxxx-xxx,,,] [,,||||,] --- [ main] i.m.c.h.jpa.EntityManagerFactoryBean : Hibernate mapping error
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on root.company.library1.domain.process.actor.ActorCreationProcess.actorType references an unknown entity: root.company.library1.domain.actor.ActorType
The entities are:
#Entity
#Table(name = 'actor_creation_process')
class ActorCreationProcess extends BaseEntity {
#JoinColumn(name = 'actor_type_id')
#ManyToOne(optional = false, targetEntity = ActorType)
ActorType actorType
}
And:
#Entity
#Table(name='actor_type')
class ActorType extends BaseEntity {
#Id
#GeneratedValue
UUID id
#Column (nullable = false)
String name
}
The superclass "BaseEntity" is as follows:
#MappedSuperclass
abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 7933099518124664248L
#Column
LocalDateTime createdAt
#Column
LocalDateTime updatedAt
#PrePersist
void onCreate() {
this.createdAt = LocalDateTime.now()
}
#PreUpdate
void onUpdate() {
this.updatedAt = LocalDateTime.now()
}
}
Sometimes I'll execute it and it won't throw the error.
Any suggestions?
Edit: Specified I'm using Groovy and BaseEntity specification.
please make sure that base entity as the following
#MappedSuperclass
public class BaseEntity {}
Also,
make sure that target entity refer to the mentioned entity from the same package not different package and it must refer to the class not the entity, i mean
#ManyToOne(optional = false, targetEntity = ActorType.class)
ActorType actorType

Relationship with a subclass of SINGLE_TABLE inheritance

I translated some hbm configuration to annotated java class. In the hbm some classes were defined with inheritance strategy "SINGLE_TABLE" and some other entity refer to it with many to one relationship as Map.
when I try to lauch the application I get the following error :
Caused by: org.hibernate.AnnotationException: Map key property not found: com.package.MyClass.Id
I searched for some explanation online, but nothing describing at the same time the SINGLE_TABLE inheritance strategy and the OneToMany behavior in this case.
I have the parent class as follows :
#Entity
#Table(name = "parentclass")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", length = 10, discriminatorType = DiscriminatorType.INTEGER)
#DiscriminatorValue("100")
public abstract class ParentClass {
#Id
#Column(name = "Id", length = 11)
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
....
}
the child class :
#Entity
#DiscriminatorValue("2")
public abstract class ChildClass {
....
}
the class with the relation :
#Entity
#Table(name = "otherclass")
#PrimaryKeyJoinColumn(name = "IdSys")
public class OtherClass extends OtherParent {
....
#OneToMany
#JoinColumn(name = "IdOther")
#MapKey(name = "Id")
#Where(clause = "type = 2")
private Map<String, ChildClass> childClassMap;
....
}
It worked when it was defined in hbm so I guess it should work with annotation.
I finally find out what was the issue.
In hbm file, the MapKey name refer to the column name. But the annotation refer to the field name.
So instead of
#MapKey(name = "Id")
I must have
#MapKey(name = "id")

java.sql.SQLException: Table 'myTable.owner' doesn't exist

I am using JPA to create and access a database. My database consists of two tables? These two tables have a #OneToMany relationship. However, when I run my application, I am getting a java.sql.SQLException.
I have done some research but none of the solutions online seem to work for me. The first solution suggests to replace #GeneratedValue(strategy=GenerationType.AUTO) with #GeneratedValue(strategy=GenerationType.IDENTITY)
The second solution consists of Including spring.jpa.hibernate.use-new-id-generator-mappings=false into application.properties.
None of these solutions worked.
Car.java
#Entity
public class Car {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
....
#ManyToOne(fetch = FetchType.LAZY )
#JoinColumn(name = "owner")
private Owner owner;
....
}
Owner.java
#Entity
public class Owner {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long ownerid;
....
#OneToMany(cascade =CascadeType.ALL, mappedBy="owner")
private List<Car> cars;
....
}
application.properties
logging.level.root=INFO
server.port=8080
spring.jpa.show-sql=true
spring.datasource.url=jdbc:mariadb://localhost:3306/cardb
spring.datasource.username=****
spring.datasource.password=*******
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.generate-dll=true
spring.jpa.hibernate.dll-auto=create-drop
Here is the Exception I am getting :
java.sql.SQLException: Table 'cardb.owner' doesn't exist
EDIT: I am using MariaDB and the database's name is cardb. So, when I run the application, it should create cardb.car, cardb.owner and cardb.car_owner
By the way, the database is properly created when using H2. Does it mean the problem comes from MariaDB ?
#JoinColumn(name = "owner")
should be
#JoinColumn(name = "ownerid")
I think you are missing #Table in your entity classes.
Define Car as,
#Entity
#Table (name="car")
public class Car {
...
}
and
Owner as below,
#Entity
#Table (name = "owner")
public class Owner {
...
}

JPA loads too much data even if fetchtype is lazy

I have an application that loads data related to a sewer network. Here is a sample of entities involved. First, I define a Network entity:
#Entity
#Table(name = "network")
public class Network extends Serializable {
#Id
#generatedValue(strategy=GenerationType.AUTO)
private Long id;
...
#OneToMany(mappedBy = "network")
private List<Conduit> conduits;
#OneToMany(mappedBy = "network")
private List<ConduitVolumeter> conduitVolumeters;
...
}
Second entity involved is Conduit, which represents a pipe in the network.
#Entity
#Table(name = "conduit")
#NamedQuery(name = "Conduit.findAll", query = "SELECT c FROM Conduit c")
public class Conduit extends Serializable {
#Id
#generatedValue(strategy=GenerationType.AUTO)
private Long id;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_network", nullable = false)
private Network network;
#OneToOne(mappedBy = "conduit", fetch = FetchType.LAZY)
private ConduitVolumeter conduitVolumeter;
...
}
Next, I define ConduitVolumeter. This represents a measuring device used to measure volume in a conduit. Since volumes can be measured in other network items, there is an abstract class Volumeter associated to the volumeter table in the database.
#Entity(name = "ConduitVolumeter")
#DiscriminatorValue("1")
public class ConduitVolumeter extends Volumeter {
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_nme")
private Conduit conduit;
...
}
#Entity
#Table(name = "volumeter")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "nme_type", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Volumeter extends Serializable {
#Id
#generatedValue(strategy=GenerationType.AUTO)
private Long id;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_network", nullable = false)
private Network network;
...
}
The problem is that when I try to load all conduits associated to a network, Hibernate tries systematically to load every volumeter associated with each loaded conduit, one at a time, according to its logs.
Hibernate:
select
conduit0_.id as id_1_7_,
conduit0_.name as name23_7_,
conduit0_.id_network as id_netw39_7_,
from
conduit conduit0_ cross
join
network network1_
where
conduit0_.id_network=?
...
Hibernate:
select
conduitvol0_.id as id2_116_0_,
conduitvol0_.name as name10_116_0_,
conduitvol0_.id_network as id_netw15_116_0_,
conduitvol0_.id_nme as id_nme17_116_0_
from
volumeter conduitvol0_
where
conduitvol0_.id_nme=?
and conduitvol0_.nme_type=1
My question : why does Hibernate try to load volumeter data? Each #ManyToOne and #OneToOne annotations is set with FetchType.LAZY. What did I miss?
BTW, I'm asked to use JPA with a legacy database. Is the database model the problem? Is there a way to load the conduits without their volumeters?
Regards.
Francois
FetchType is a just a hint. It depends upon how you are querying the data, if you are using spring-data-jpa repository methods like findOne or findBy methods(derived queries) then Lazy should work, but if you are using JPA repository with #Query on repository method and write your query then Lazy might not work.

Hibernate. Repeated column when mapping #IdClass annotated entity with composite key

I've ran into problem with composite primary key handling by Hibernate as a JPA provider.
My entities look like below
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#Column(name = "place_id")
private Integer placeId;
#Id
#Column(name = "external_object_id")
private Integer externalObjectId;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Integer placeId;
private Integer externalObjectId;
}
Looks pretty simple yet no matter what I do I keep getting the following exception (lines are splitted for readability):
org.hibernate.MappingException:
Repeated column in mapping for entity: ExternalMatch
column: external_object_id (should be mapped with insert="false" update="false")
I've tried placing annotation on entity class fields and key class fields together as well as separately, moving all annotations from fields to getters on each one of the classes, using key calss as #Embeddable and putting it into the entity class with #EmbeddedId. Nothing seems to work.
This case seems trivial so maybe it's something wrong with our setup but I can't even imagine where to look for the issue.
Any advice is much appreciated.
It appears that I shot myself in the foot with this.
The issue was that I had a biderectional mapping between ExternalMatch and ExternalObject I forgot about trying to replace the actual entity with its integer id.
So changing
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#Column(name = "place_id")
private Integer placeId;
#Id
#Column(name = "external_object_id")
private Integer externalObjectId;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Integer placeId;
private Integer externalObjectId;
}
// Related entity class
#Entity
#Table(name = "external_object")
public class ExternalObject extends AbstractNameableEntity {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "external_object_id", nullable = false)
private List<ExternalMatch> matches;
// ...
}
to reprsent actual mappings like this
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#ManyToOne
#JoinColumn(name = "external_object_id", referencedColumnName = "id")
private ExternalObject externalObject;
#Id
#ManyToOne
#JoinColumn(name = "place_id")
private Poi place;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Poi place;
private ExternalObject externalObject;
}
// Related entity class
#Entity
#Table(name = "external_object")
public class ExternalObject extends AbstractNameableEntity {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "externalObject")
private List<ExternalMatch> matches;
// ...
}
resolved the repeated mapping issue yet leaving us with all the familiar troubles a biderectional mapping creates :)

Categories

Resources