I am struggling with my inheritance structure where I have a mapped superclass which contains a common field in the concrete classes. This superclass has a one-to-one mapping with a "wrapper" object.
The objects look like this;
#Entity
public class Wrapper {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "wrapper_id", nullable = false)
private Long wrapperId;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "wrapper")
#Cascade(CascadeType.SAVE_UPDATE)
private Base base;
public Long getWrapperId() {
return wrapperId;
}
public void setWrapperId(Long wrapperId) {
this.wrapperId = wrapperId;
}
public Base getBase() {
return base;
}
public void setBase(Base base) {
this.base = base;
}
}
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Base {
#OneToOne(fetch = FetchType.LAZY)
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name = "wrapper_id")
protected Wrapper wrapper;
public Wrapper getWrapper() {
return wrapper;
}
public void setWrapper(Wrapper wrapper) {
this.wrapper = wrapper;
}
}
#Entity
public class SubA extends Base {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "sub_a_id", nullable = false)
private Long subAId;
public Long getSubAId() {
return subAId;
}
public void setSubAId(Long subAId) {
this.subAId = subAId;
}
}
For simplicity I have only included a single concrete class but I have several.
This mapping works great when I do not have a reference to "Base" in the wrapper object. As soon as I try to add the bi-directional relationship between the wrapper and base I start to get this error....which doesn't make sense as the field is there.
Caused by: org.hibernate.AnnotationException: Unknown mappedBy in: com.xxx.Wrapper.base, referenced property unknown: com.xxx.Base.wrapper
at org.hibernate.cfg.OneToOneSecondPass.doSecondPass(OneToOneSecondPass.java:153)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1697)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.buildSessionFactory(LocalSessionFactoryBuilder.java:372)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:453)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:438)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1627)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1564)
... 50 more
What am I missing?
Thanks,
Quote from Java Platform, Enterprise Edition: The Java EE Tutorial:
37.2.2 Mapped Superclasses
Entities may inherit from superclasses that contain persistent state
and mapping information but are not entities. That is, the superclass
is not decorated with the #Entity annotation and is not mapped as an
entity by the Java Persistence provider. These superclasses are most
often used when you have state and mapping information common to
multiple entity classes. Mapped superclasses are specified by
decorating the class with the annotation
javax.persistence.MappedSuperclass:
...
Mapped superclasses cannot be queried and cannot be used in
EntityManager or Query operations. You must use entity subclasses of
the mapped superclass in EntityManager or Query operations. Mapped
superclasses can't be targets of entity relationships.
So it looks like you can`t use this Base class in entity relationships:
#OneToOne(fetch = FetchType.LAZY, mappedBy = "wrapper")
#Cascade(CascadeType.SAVE_UPDATE)
private Base base;
Looks like this hibernate bug: Hibernate complains about an unknown mappedBy property when mapping a bidirectional OneToOne relation with a derived identifier, which was fixed only in late hibernate 4.2.2, 4.3.0.Beta3 versions.
We ended up doing a lot of prototyping with the different options (mapped super class, hierarchical etc) and weighed the options.
In the end we decided to create object hierarchies coupled with the #Entity annotation, Inheritance strategy of SINGLE_TABLE and using Discriminator values to give us exactly what we needed without sacrificing too much.
Thank you all for your suggestions.
Related
I am trying to store a entity inside a Database with hibernate. I have got the following classes:
#Entity
public class UsableRemoteExperiment extends RemoteExperiment {
private List<ExperimentNodeGroup> nodeGroups = new ArrayList<>();
#OneToMany(mappedBy = "experiment", cascade = CascadeType.ALL, orphanRemoval = true)
public List<ExperimentNodeGroup> getNodeGroups() {
return nodeGroups;
}
public void setNodeGroups(final List<ExperimentNodeGroup> nodeGroups) {
this.nodeGroups = nodeGroups;
}
/* More getters and setters for other attributes */
The Experiment Node Group looks like this:
#Entity
public class ExperimentNodeGroup extends NodeGroup {
private List<Node> nodes = new ArrayList<>();
/* More getters and setters for other attributes */
And the NodeGroup Class looks like this:
#Entity
public abstract class NodeGroup extends GeneratedIdEntity {
protected Experiment experiment;
#ManyToOne(optional = false)
#JsonIgnore
public Experiment getExperiment() {
return experiment;
}
/* More getters and setters for other attributes */
Now when i try to compile the Code, I get this error:
Caused by: org.hibernate.AnnotationException: mappedBy reference an
unknown target entity property:
[...].ExperimentNodeGroup.experiment
in
[...].UsableRemoteExperiment.nodeGroups
It's one of the quirks of the hibernate where it does not work as expected with mappedBy and inheritance. Could you try specifying targetEntity as well? Here's the documentation and this is what it says:
The entity class that is the target of the association. Optional only
if the collection property is defined using Java generics. Must be
specified otherwise.
You can try specifying targetEntity = ExperimentNodeGroup.class or targetEntity = Transaction.class and see if that makes any difference.
I think the problem here is that you need to also put on a setter, hibernate assumes that if you have a getter to get from a database you will need a setter to read from it, you can either put a setter, or use
#Entity(access = AccessType.FIELD)
and put the annotations on your attributes.
I am creating entities that are the same for two different tables. In order do table mappings etc. different for the two entities but only have the rest of the code in one place - an abstract superclass. The best thing would be to be able to annotate generic stuff such as column names (since the will be identical) in the super class but that does not work because JPA annotations are not inherited by child classes. Here is an example:
public abstract class MyAbstractEntity {
#Column(name="PROPERTY") //This will not be inherited and is therefore useless here
protected String property;
public String getProperty() {
return this.property;
}
//setters, hashCode, equals etc. methods
}
Which I would like to inherit and only specify the child-specific stuff, like annotations:
#Entity
#Table(name="MY_ENTITY_TABLE")
public class MyEntity extends MyAbstractEntity {
//This will not work since this field does not override the super class field, thus the setters and getters break.
#Column(name="PROPERTY")
protected String property;
}
Any ideas or will I have to create fields, getters and setters in the child classes?
Thanks,
Kris
You might want to annotate MyAbstractEntity with #MappedSuperclass class so that hibernate will import the configuration of MyAbstractEntity in the child and you won't have to override the field, just use the parent's. That annotation is the signal to hibernate that it has to examine the parent class too. Otherwise it assumes it can ignore it.
Here is an example with some explanations that may help.
#MappedSuperclass:
Is a convenience class
Is used to store shared state & behavior available to child classes
Is not persistable
Only child classes are persistable
#Inheritance specifies one of three mapping strategies:
Single-Table
Joined
Table per Class
#DiscriminatorColumn is used to define which column will be used to distinguish between child objects.
#DiscriminatorValue is used to specify a value that is used to distinguish a child object.
The following code results in the following:
You can see that the id field is in both tables, but is only specified in the AbstractEntityId #MappedSuperclass.
Also, the #DisciminatorColumn is shown as PARTY_TYPE in the Party table.
The #DiscriminatorValue is shown as Person as a record in the PARTY_TYPE column of the Party table.
Very importantly, the AbstractEntityId class does not get persisted at all.
I have not specified #Column annotations and instead are just relying on the default values.
If you added an Organisation entity that extended Party and if that was persisted next, then the Party table would have:
id = 2
PARTY_TYPE = "Organisation"
The Organisation table first entry would have:
id = 2
other attribute value associated specifically with organisations
#MappedSuperclass
#SequenceGenerator(name = "sequenceGenerator",
initialValue = 1, allocationSize = 1)
public class AbstractEntityId implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "sequenceGenerator")
protected Long id;
public AbstractEntityId() {}
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PARTY_TYPE",
discriminatorType = DiscriminatorType.STRING)
public class Party extends AbstractEntityId {
public Party() {}
}
#Entity
#DiscriminatorValue("Person")
public class Person extends Party {
private String givenName;
private String familyName;
private String preferredName;
#Temporal(TemporalType.DATE)
private Date dateOfBirth;
private String gender;
public Person() {}
// getter & setters etc.
}
Hope this helps :)
Mark the superclass as
#MappedSuperclass
and remove the property from the child class.
Annotating your base class with #MappedSuperclass should do exactly what you want.
This is old, but I recently dealt with this and would like to share my solution. You can add annotations to an overridden getter.
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Column(name = "id", nullable = false, updatable = false)
#Id
private ID id;
public ID getId() {
return id;
}
...
}
#Entity
#Table(name = "address")
public final class Address extends AbstractEntity<UUID> implements Serializable {
...
#Override
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
public final UUID getId() {
return super.getId();
}
...
}
I have an entity Ride which embeds an embeddable "entity" Route. Route has a List property towns with ManyToMany relation, so it has fetchtype LAZY (and I don't want to use EAGER). So I want to define an NamedEntityGraph for the entity Ride, to load load a Ride object with a Route with instantied List of towns.
But when I deploy my war, I get this exception:
java.lang.IllegalArgumentException: Attribute [route] is not of managed type
Ride
#Entity
#NamedQueries({
#NamedQuery(name = "Ride.findAll", query = "SELECT m FROM Ride m")})
#NamedEntityGraphs({
#NamedEntityGraph(
name = "rideWithInstanciatedRoute",
attributeNodes = {
#NamedAttributeNode(value = "route", subgraph = "routeWithTowns")
},
subgraphs = {
#NamedSubgraph(
name = "routeWithTowns",
attributeNodes = {
#NamedAttributeNode("towns")
}
)
}
)
})
public class Ride implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Embedded
private Route route;
// some getter and setter
}
Route
#Embeddable
public class Route implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToMany
private List<Town> towns;
// some getter and setter
}
Looking at Hibernate's implementation of org.hibernate.jpa.graph.internal.AttributeNodeImpl lead us to the conclusion that #NamedAttributeNode cannot be:
simple types (Java primitives and their wrappers, strings, enums, temporals, ...)
embeddables (annotated with #Embedded)
element collections (annotated with #ElementCollection)
if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC ||
attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED ) {
throw new IllegalArgumentException(
String.format("Attribute [%s] is not of managed type", getAttributeName())
);
}
I didn't find similar restriction in JPA 2.1 spec, therefore it may be Hibernate's shortcoming.
In your particular case the problem is that #NamedEntityGraph refers to the Route class which is an embeddable, thus its usage in the entity graph seems to be forbidden by Hibernate (unfortunately).
In order to make it work you would need to change your entity model a little. A few examples that come into my mind:
define Route as entity
remove Route and move its towns field into Ride entity (simplifies the entity model)
move route field from Ride into Town entity, add map of routedTowns map to Ride entity:
#Entity
public class Ride implements Serializable {
...
#ManyToMany(mappedBy = "rides")
private Map<Route, Town> routedTowns;
...
}
#Entity
public class Town implements Serializable {
...
#ManyToMany
private List<Ride> rides;
#Embeddable
private Route route;
...
}
Of course the entity graph may require changes accordingly.
I would like to know if there exists some way to use generics in JPA 2.0?
Consider this scenario:
#Entity
public class GenericPhoto<T> implements Serializable {
#Id
#GeneratedValue
private long id;
#NotNull
private byte[] file;
#ManyToOne(cascade = { CascadeType.DETACH })
#JoinColumn(name = "PARENTID", nullable = false)
#NotNull
private T parent;
//...
}
#Entity
public Car {
#OneToMany(mappedBy = "parent")
private Set<GenericPhoto<Car>> photos;
//...
}
#Entity
public Truck {
#OneToMany(mappedBy = "parent")
private Set<GenericPhoto<Truck>> photos;
//...
}
I hope the code explains it all. I simply want to make a generic class for photo, which I think makes it easier to implement services etcetera.
Best regards
When you use a generic, it is similar to not typing the field (i.e. Object parent), so you need to tell JPA how to map the relationship. For this you can use targetEntity in JPA.
For this to work, you will need a common superclass to Car and Truck i.e. Auto, and set the targetEntity in the #ManyToOne to Auto (you may also consider moving photos up to Auto).
If you can't use inheritance for some reason, (it is best to use inheritance). Then if you use EclipseLink you could use a #VariableOneToOne relationship.
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>();