I have a problem trying to map an inheritance tree. A simplified version of my model is like this:
#MappedSuperclass
#Embeddable
public class BaseEmbedded implements Serializable {
#Column(name="BE_FIELD")
private String beField;
// Getters and setters follow
}
#MappedSuperclass
#Embeddable
public class DerivedEmbedded extends BaseEmbedded {
#Column(name="DE_FIELD")
private String deField;
// Getters and setters follow
}
#MappedSuperclass
public abstract class BaseClass implements Serializable {
#Embedded
protected BaseEmbedded embedded;
public BaseClass() {
this.embedded = new BaseEmbedded();
}
// Getters and setters follow
}
#Entity
#Table(name="MYTABLE")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE", discriminatorType=DiscriminatorType.STRING)
public class DerivedClass extends BaseClass {
#Id
#Column(name="ID", nullable=false)
private Long id;
#Column(name="TYPE", nullable=false, insertable=false, updatable=false)
private String type;
public DerivedClass() {
this.embedded = new DerivedClass();
}
// Getters and setters follow
}
#Entity
#DiscriminatorValue("A")
public class DerivedClassA extends DerivedClass {
#Embeddable
public static NestedClassA extends DerivedEmbedded {
#Column(name="FIELD_CLASS_A")
private String fieldClassA;
}
public DerivedClassA() {
this.embedded = new NestedClassA();
}
// Getters and setters follow
}
#Entity
#DiscriminatorValue("B")
public class DerivedClassB extends DerivedClass {
#Embeddable
public static NestedClassB extends DerivedEmbedded {
#Column(name="FIELD_CLASS_B")
private String fieldClassB;
}
public DerivedClassB() {
this.embedded = new NestedClassB();
}
// Getters and setters follow
}
At Java level, this model is working fine, and I believe is the appropriate one. My problem comes up when it's time to persist an object.
At runtime, I can create an object which could be an instance of DerivedClass, DerivedClassA or DerivedClassB. As you can see, each one of the derived classes introduces a new field which only makes sense for that specific derived class. All the classes share the same physical table in the database. If I persist an object of type DerivedClass, I expect fields BE_FIELD, DE_FIELD, ID and TYPE to be persisted with their values and the remaining fields to be null. If I persist an object of type DerivedClass A, I expect those same fields plus the FIELD_CLASS_A field to be persisted with their values and field FIELD_CLASS_B to be null. Something equivalent for an object of type DerivedClassB.
Since the #Embedded annotation is at the BaseClass only, Hibernate is only persisting the fields up to that level in the tree. I don't know how to tell Hibernate that I want to persist up to the appropriate level in the tree, depending on the actual type of the embedded property.
I cannot have another #Embedded property in the subclasses since this would duplicate data that is already present in the superclass and would also break the Java model.
I cannot declare the embedded property to be of a more specific type either, since it's only at runtime when the actual object is created and I don't have a single branch in the hierarchy.
Is it possible to solve my problem? Or should I resignate myself to accept that there is no way to persist the Java model as it is?
Any help will be greatly appreciated.
Wow. This is the simplified version? I assume that the behavior that you are seeing is that BaseEmbedded field is persisted but not the FIELD_CLASS_A or B?
The problem is that when Hibernate maps the DerivedClassA and B classes, it reflects and sees the embedded field as a BaseEmbedded class. Just because you then persist an object with the embedded field being a NestedClass, the mapping has already been done and the FIELD_CLASS_A and B are never referenced.
What you need to do is to get rid of the NestedClass* and embedded field and instead have the fieldClassA and B be normal members of DerivedClassA and B. Then add add a name field to the #Entity which will put them both in the same table I believe. This will allow you to collapse/simplify your class hierarchy a lot further.
See: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e1168
#Entity(name = "DerivedClass")
#DiscriminatorValue("A")
public class DerivedClassA extends DerivedClass {
#Column(name="FIELD_CLASS_A")
private String fieldClassA;
...
#Entity(name = "DerivedClass")
#DiscriminatorValue("B")
public class DerivedClassB extends DerivedClass {
#Column(name="FIELD_CLASS_B")
private String fieldClassB;
...
Related
I want to use Java records as embeddable objects with JPA. For example I want to wrap the ID in a record to make it typesafe:
#Entity
public class DemoEntity {
#EmbeddedId
private Id id = new Id(UUID.randomUUID());
#Embeddable
public static record Id(#Basic UUID value) implements Serializable {}
}
But If I try to persist it with Hibernate 5.4.32 I get the following error:
org.hibernate.InstantiationException: No default constructor for entity: : com.example.demo.DemoEntity$Id
at org.hibernate.tuple.PojoInstantiator.instantiate(PojoInstantiator.java:85) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.tuple.component.AbstractComponentTuplizer.instantiate(AbstractComponentTuplizer.java:84) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
...
So it looks like Hibernate would treat the record Id like an entity, although it is an #Embeddable.
The same happens with non-id fields and #Embedded:
#Embedded
private Thing thing = new Thing("example");
#Embeddable
public static record Thing(#Basic String value) implements Serializable {}
Is there a way to use #Embeddable records with JPA/Hibernate?
Java records with a single field can be used for custom ID types or any other value object with AttributeConverters.
In the entity class the ID type is used with #Id as usual:
#Entity
public class DemoEntity {
#Id
private Id id = new Id(UUID.randomUUID());
public static record Id(UUID value) implements Serializable {}
}
Note that the record Id doesn't have any annotation.
The converter makes it possible to use records:
#Converter(autoApply = true)
public class DemoEntityIdConverter implements AttributeConverter<DemoEntity.Id, String> {
#Override
public String convertToDatabaseColumn(DemoEntity.Id id) {
return id.value().toString();
}
#Override
public DemoEntity.Id convertToEntityAttribute(String s) {
return new DemoEntity.Id(UUID.fromString(s));
}
}
Don't forget to set autoApply = true to have this converter applied automatically (without referencing it explicitly on the respective field).
Records with more than one field could be mapped with a Hibernate UserType, but that is a bit cumbersome.
Entity or embeddable, in any case the record class wouldn't be suitable here because entities and their fields, including embeddable ones, are modifiable. The only exception would be for Id fields, but that doesn't seem like an important enough case to make this functionality for.
One of the Hibernate developers explains this here
I have a project that I've recently inherited that has 2 tables that share a lot of common fields. I'm new to hibernate and want to know if I can use composition to generate the table instead of inheritance? B and D are basically the same class with a different table name.
Current Hierarchy is
B extends A extends BaseClass
D extends C extends BaseClass
My problem at the moment is that a lot of other classes extend BaseClass which don't have the shared fields and the 2 child classes don't share a common parent so I cannot add another level into the Hierarchy and use #MappedSuperclass.
Because of this I'd like to know if I can group my common fields into a single class and compose my child classes with this new class somehow?
Apologies for the cryptic names but as always; confidentiality...
edit - found something simmilar with #Embeddable https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/domain/embeddables.html
If you are using the JPA interface to Hibernate, you can use #Embedded and #Embeddable, to get more or less what you want. Be aware that the change will not be transparent: where you had:
#Entity
public class B extends A {
#Basic
private int foo;
...
}
that you referenced in JPQL by using b.foo, you will have:
#Embeddable
public classs Common {
#Basic
private int foo;
...
}
#Entity
public class B extends A {
#Embedded
private Common common;
...
}
which you will have to reference in JPQL using b.common.foo.
Read more about embeddable entities here:
https://en.wikibooks.org/wiki/Java_Persistence/Embeddables
You could potentially use #Embedded and embed the same object for both B and D, perhaps something like:
#Embeddable
public class CommonFieldObject {
#Column(name="COMFIELD1")
private String commonField1;
#Column(name="COMFIELD2")
private String commonField2;
...
}
#Table
public class C extends A {
#Embedded
#AttributeOverrides({
#AttributeOverride(name="commonField1", column=#Column(name="CFO_COMFIELD1")),
#AttributeOverride(name="commonField2", column=#Column(name="CFO_COMFIELD2"))
})
private CommonFieldObject commonFieldObj; //CFO_ prefix for this reference - in case we have a second field referencing a CommonFieldObject - use a different prefix..
...
}
You should then get the columns CFO_COMFIELD1 and CFO_COMFIELD2 in your table and you can recycle the CommonFieldObject for class D.
I have a Java Entity that has several well defined functors. I want to persist them so as not factorize the object once it is fetched from the database. Is there a way to do so with Ebean?
I think that I could get it saving the class name that implements those functors as a String in the entity and in the setter of that string implement the setter of the functor with Reflection. Any other idea?
Class Example:
#Entity
public Foo extends Model
{
#Id
private Long id;
#Transient
private Runnable functor;
private String classFunctor;
public void setClassFunctor(String value)
{
//Here I implement the Reflection routine to load the functor.
}
}
In my Hibernate+Spring project I've got several entities quite similar; that's why I'm using inheritance type: Table Per Class. The thing is similar to this
class CommonEntity {
private Integer id;
private String name;
private String description;
// Constructors & Setters & Getters
}
class InheritedClass1 extends CommonEntity {
private boolean active;
// Constructors & Setters & Getters
}
class InheritedClass2 extends CommonEntity {
}
As I'm using XML mapping, the mapping for the 1st inherited class contains both CommonEntity and InheritedClass1 fields/columns.
Everything's fine so far.
Here the question is, what would be the best way to implement Repositories/DAOs for the inherited entities? As they will contain common fields (for instance, probably it would end up in implementing a findByName DAO method).
My main point is to avoid implement the same findByName for all inherited class as the only difference would be the name of the Named Query (i.e. InheritedClass1_FindByName and InheritedClass2FindByName).
You could use generics to make a single DAO method that would work for any class which extends your CommonEntity like this:
public <T extends CommonEntity > T getByName(Class<T> clazz, String name) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(clazz, "named");
crit.add(Restrictions.eq("named.name", name));
return (T)crit.uniqueResult();
}
Obviously if name is not unique you would need to call crit.list() rather than uniqueResult()
Suppose a Table per subclass inheritance relationship which can be described bellow (From wikibooks.org - see here)
Notice Parent class is not abstract
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Project {
#Id
private long id;
// Other properties
}
#Entity
#Table(name="LARGEPROJECT")
public class LargeProject extends Project {
private BigDecimal budget;
}
#Entity
#Table(name="SMALLPROJECT")
public class SmallProject extends Project {
}
I have a scenario where i just need to retrieve the Parent class. Because of performance issues, What should i do to run a HQL query in order to retrieve the Parent class and just the Parent class without loading any subclass ???
A workaround is described below:
Define your Parent class as MappedSuperClass. Let's suppose the parent class is mapped To PARENT_TABLE
#MappedSuperClass
public abstract class AbstractParent implements Serializable {
#Id
#GeneratedValue
private long id;
#Column(table="PARENT_TABLE")
private String someProperty;
// getter's and setter's
}
For each subclass, extend the AbstractParent class and define its SecondaryTable. Any persistent field defined in the parent class will be mapped to the table defined by SecondaryTable. And finally, use AttributeOverrides if needed
#Entity
#SecondaryTable("PARENT_TABLE")
public class Child extends AbstractParent {
private String childField;
public String getChildProperty() {
return childField;
}
}
And define another Entity with the purpose of retrieving just the parent class
#Entity
#Table(name="PARENT_TABLE")
#AttributeOverrides({
#AttributeOverride(name="someProperty", column=#Column(name="someProperty"))
})
public class Parent extends AbstractParent {}
Nothing else. See as shown above i have used just JPA specific annotations
Update: It appears the first option doesn't work as I thought.
First option:
Specify the class in the where clause:
select p from Project p where p.class = Project
Second option:
Use explicit polymorphism that you can set using Hibernate's #Entity annotation:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)
#Inheritance(strategy = InheritanceType.JOINED)
public class Project {
#Id
private long id;
...
}
This is what Hibernate Core documentation writes about explicit polymorphism:
Implicit polymorphism means that
instances of the class will be
returned by a query that names any
superclass or implemented interface or
class, and that instances of any
subclass of the class will be returned
by a query that names the class
itself. Explicit polymorphism means
that class instances will be returned
only by queries that explicitly name
that class.
See also
How to get only super class in table-per-subclass strategy?
Actually, there is a way to get just the superclass, you just need to use the native query from JPA, in my case I'm using JPA Repositories it would be something like that:
#Query(value = "SELECT * FROM project", nativeQuery = true)
List<Resource> findAllProject();
The flag nativeQuery as true allow running the native SQL on database.
If you are using Entity Manager check this out: https://www.thoughts-on-java.org/jpa-native-queries/