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")
Related
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.
I have the following class:
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
public class Yard extends ModelObject {
// Relations
#ManyToOne(optional=false)
#JoinColumn(name = "house_id", foreignKey=#ForeignKey(name="fk1_yard"))
#Getter #Setter
#JsonView({Views.AdminPortal.class})
private House house = null;
}
And I get the following error
Could not write JSON: Unable to find com.db.model.main.House with id 7
My understanding was that targetAuditMode = RelationTargetAuditMode.NOT_AUDITED would prevent this error by only auditing the id number. What am I doing wrong here?
Your usage is incorrect. I believe what you're looking for is:
#Audited
#Entity
public class Yard extends ModelObject {
#ManyToOne(optional=false)
#JoinColumn(name = "house_id", foreignKey=#ForeignKey(name="fk1_yard"))
#Getter
#Setter
#JsonView({Views.AdminPortal.class})
#Audited(targetAuditMode = RElationTargetAuditMode.NOT_AUDITED)
private House house = null;
}
You'll notice how the #Audited annotation with targetAuditMode is being used on the association in the entity mapping rather than on the class-mapping. That attribute has no impact at the class-level.
Premise:
I have been recently assigned to work with a Java + JPA + Hibernate application.
This application has 4 different "modules" and each is a "copy+paste" of each other, with minor changes.
I want to remove all duplications and work with a single database schema (currently, there is one schema for each "module").
I am trying to start in the least "invasive" way, not changing too much of what's already there.
What I did was:
I created a base module and moved some hibernate entities there.
I made these entities abstract and created implementations for each module.
I create a new schema and moved every other module record to it (I had to disable the database constraints for now).
Example:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "SYSTEM", length = 10, discriminatorType = DiscriminatorType.STRING)
#Table(name = "GROUP")
public abstract class Group<U extends UserGroup> {
#Id
private Integer id;
#Column(name = "CODE")
private String code;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "group")
private List<U> users;
#Entity
#Table(name = "USER_GROUP")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "SYSTEM", length = 10, discriminatorType = DiscriminatorType.STRING)
public abstract class UserGroup<G extends Group> {
#Id
private Integer id;
#Column(name = "name")
private String name;
#ManyToOne(optional=false)
#JoinColumn(name = "GROUP_ID", referencedColumnName = "ID", insertable=false, updatable=false)
private G group;
The implementations just define a discriminator column.
Issue:
The following query:
public interface UserGroupRepository<T extends UserGroup> extends CrudRepository<T, Integer> {
#Query(value = "select grp.code from #{#entityName} ug join ug.group grp where ug.name= ?1")
Iterable<String> listGroupByUser(String name);
Is returning 4 items because my user has a record for each module in the database (it should return only 1 item).
Question:
Using "#Query", can I somehow filter by the discriminator value properly?
According to Hibernate Inheritance doc, you can get this by table per subclass instead of table per class hierarchy strategy.
Yes you can try using the where clause. There is a special class property that you can use to restrict a query to a subtype. Below is the hibernate reference documentation.
In your case you can use the subEntity name instead of DomesticCat.
Click here to see the hibernate documentation of the where clause.
I have DisseminationArea as subcalss for Feature with the following code:
#Entity
#Table(name = "features")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "subtype_id", discriminatorType = DiscriminatorType.INTEGER)
public class Feature {
#Id
#Column(name="id")
#GeneratedValue(generator="sqlite")
#TableGenerator(name="sqlite", table="sqlite_sequence",
pkColumnName="name", valueColumnName="seq",
pkColumnValue="features")
#Getter
#Setter
private long id;
#ManyToOne
#JoinColumn(name = "subtype_id")
#Getter
#Setter
private FeatureSubtype featureSubtype;
#ManyToOne
#JoinColumn(name = "parent_id")
#Getter
#Setter
private Feature parent;
...
}
Unfortunately, this causes an exception when save this entity to database, because subtype_id field is used twice.
Can I annotate it somehow so that JPA know it is the same field?
If a discriminator column makes sense with InheritanceType.JOINED is worth discussing. I would tend to omit it on joined strategy and only use it with InheritanceType.SINGLE_TABLE.
Nevertheless, it's possible to map it without duplicate column errors.
If your superclass entity has an inheritance / discriminator definition like:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "subtype_id", discriminatorType = DiscriminatorType.INTEGER)
You just have to adjust the mapping to not update the value as usual by setting it readonly:
#Column(name="subtype_id", insertable = false, updatable = false)
protected int subTypeId;
public int getSubTypeId() {
return subTypeId;
}
Now you can access the discriminator value of the entity.
Same column name is used for relation FK to FeatureSubtype.
Use other name for discriminator or don't use discriminator at all.
In Hibernate, discriminator column on joined inheritance is supported but not required. Hibernate is querying all subtables to determine correct subclass.
Or use for example:
th:text="${OBJECTNAME.class.getSimpleName()}"
This is far simple the using #DiscriminatorColumn...
I am trying to map a bi-directional one-to-many relationship. I am having some trouble as the "many" side references an abstract superclass. While searching the internet for possible causes I discovered that this is a known problem but I wasn't able to find a solution for my case.
I have checked the workarounds on this blog and the "Single table, without mappedBy" looks like a solution but I really need the bi-directional association.
These are the classes I am trying to map:
Owning Side
#Entity(name = "CC_Incl_Site")
public class IncludedSite {
#OneToMany(fetch=FetchType.LAZY, mappedBy = "includedSite")
private Set<CtaContractBase> ctas = new HashSet<CtaContractBase>();
#OneToMany(fetch=FetchType.LAZY, mappedBy = "includedSite")
private Set<WoContractBase> wos = new HashSet<WoContractBase>();
}
Other Side:
#Entity
public abstract class SCContract extends Contract {
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "incl_site_id")
private IncludedSite includedSite;
}
Contract (the superclass of SCContract):
#Entity(name = "CC_CONTRACT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "contractType", discriminatorType = DiscriminatorType.STRING)
#ForceDiscriminator
public abstract class Contract {
...
}
When trying to run the application I get this exception:
mappedBy reference an unknown target entity property:
CtaContractBase.includedSite in IncludedSite.ctas
Another solution appears to be replacing the #Entity annotation in SCContract with #MappedSuperClass but this results in another exception (Use of #OneToMany or #ManyToMany targeting an unmapped class: StudyContract.contracts[SCContract]) because in another class (StudyContract) I have
#OneToMany(fetch = FetchType.LAZY, mappedBy = "studyContract", targetEntity = SCContract.class)
#BatchSize(size = 10)
private Set<SCContract> contracts;
and as the blog explains having a collection of the superclass is not possible anymore using this approach.
Are there any other workarounds or am I missing something?
The association in IncludedSite is defined as
#OneToMany(fetch=FetchType.LAZY, mappedBy = "includedSite")
private Set<CtaContractBase> ctas = new HashSet<CtaContractBase>();
So Hibernate looks for an attribute of type IncludedSite named includedSite in the class CtaContractBase. There is no such field. The field only exists in the subclass SCContract. This means that only SCContract instances can be the target of this association, and the association should thus be defined as
#OneToMany(fetch=FetchType.LAZY, mappedBy = "includedSite")
private Set<SCContract> ctas = new HashSet<SCContract>();