I need to implement entity property lazy loading. I came with single table inheritance approach.
#Entity
#Table(name = "person")
#Getter
public class Person {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "firstname")
private String firstName;
}
#Entity
#Getter
public class VerbosePerson extends Person {
#Column(name = "lastname")
private String lastName;
}
public interface PersonRepository extends JpaRepository<Person, Long> {}
public interface VerbosePersonRepository extends JpaRepository<VerbosePerson, Long> {}
Unfortunately, this only works with a discriminator column. Actually, I don't need to distinguish these two entities. All that requires is to exclude lastName column from the Person fetching and to load it only when VerbosePerson is being requested.
One solution is to declare #MappedSuperClass that should have two inherited entities (Person and VerbosePerson). But in this case, Person won't be a supertype for VerbosePerson which is not convenient.
Is there any way to use single table strategy inheritance without discriminators?
It sounds like you need lazy querying and not the inheritance. You should take a look at FetchType annotations
https://thorben-janssen.com/entity-mappings-introduction-jpa-fetchtypes/
Be warned though.. these are primarily used to manage lazy loading for Lists (things that can be easily proxied). Lazily associating a single item (i.e. a #ManyToOne or a simple string, etc.) usually requires some careful object proxying under the covers to ensure it works the way you think it should in your persistence framework. Generally I didn't use it much but I think I did at one point or another to lazily load a class that had variables mapped to a row of a hibernate object that I lazily loaded..
Check out:
https://thorben-janssen.com/lazy-load-non-relational-attributes/#:~:text=The%20JPA%20specification%20defines%20the,value%20must%20be%20eagerly%20fetched.
Pay special attention to the parts:
practices, that means that depending on your JPA implementation, annotating an attribute with #Basic(fetch=FetchType.LAZY) isn’t enough.
Lazy loading for fields requires bytecode enhancement. Then you can use #Basic(fetch = LAZY) and the field will be lazy loaded on first access. Also see https://docs.jboss.org/hibernate/stable/orm/userguide/html_single/Hibernate_User_Guide.html#BytecodeEnhancement
Related
When writing documents to Mongodb using Morphia, a structure like this will be written without any problems, and without needing the #Embedded annotation:
#Entity
public class Blog {
#Id
private String id;
private List<Comment> comments;
}
The comments field is happily stored as an array of nested Comment elements (Comment is a plain POJO with no annotations).
However, the Morphia documentation suggests I should be using this:
#Entity
public class Blog {
#Id
private String id;
#Embedded
private List<Comment> comments;
}
But in my tests using the #Embedded annotation doesn't appear to do anything additional over simply writing the structure without the annotation.
So what does the #Embedded annotation actually do? Does it affect the ability to query, or index, or some other function of storage other than simply writing the data?
Serializable is not generally used with Morphia. #Embedded is a bit of an ambiguous item whose original intent seems to have gotten lost. In fact, in my fork which I'm working on making the official 2.0 of Morphia, I've restricted it such that it only applies at the class level. This tells Morphia to map the type but not fail the mapping on a missing #Id annotation. In that branch if you wish to specify a name other than the field name, you'd simply use #Property as you would for any non-embedded types.
I hope this clarifies it a little bit at least.
As a rule of thumb, you should #Embedded for objects that are dependent on the parent object (and therefore have no life outside it), and are not shared between objects.
By default, Morphia uses the field name as the value name in Mongo. This can be overridden by specifying a name on the #Embedded annotation:
#Embedded("blog_comments")
private List<Comment> comments;
Even without #Embedded you can still use java class as field type as long as that class implements java.io.Serializable. However that field will be stored in MongoDB in binary format instead of structured data as shown above.
Sources here and here
I have the following mapping:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {
#Id
#GeneratedValue
Long id;
}
#Entity
#Table(name = "car")
#AttributeOverride(name = "id", column = #Column(name = "car_id"))
public class Car extends Vehicle {
}
#Entity
#Table(name = "bus")
#AttributeOverride(name = "id", column = #Column(name = "bus_id"))
public class Bus extends Vehicle {
}
And what I'm trying to achieve is to query different tables for retrieving both Car and Bus entities. For this sake I've created the following Spring Data repository
public interface VehicleRepository extends CrudRepository<Vehicle, Long> {
}
and try to use it like this: vehicleRepository.findAll();
However, in this case I get java.sql.SQLSyntaxErrorException: ORA-00904: "KEY": invalid identifier. Seems like using #Inheritance together with #AttributeOverride for #Id field doesn't work.
What I'd like to point out is that if Car and Bus entities had the same mapping for #Id it would work perfectly (but it's not the case: "car_id" and "bus_id")
Also, I've tried to move #Id field from Vehicle class to subclasses, however it turned out that every #Entity should contain an #Id.
One more thing I'd like to mention is that I've tried using #MappedSuperclass instead of #Inheritance but in this case I'm not able to query with abstact Vehicle type.
Could anyone help me with that?
Thanks
You say,
And what I'm trying to achieve is to query different tables for retrieving both Car and Bus entities.
, but as a first consideration, you should evaluate whether you really want to do that. Think about this:
The single-table inheritance strategy is generally the fastest for whole-hierarchy queries such as you imagine performing. It can perform both whole-hierarchy and concrete-entity operations with single queries, without joins or unions.
The single-table and joined inheritance strategies ensure that all entities in the hierarchy have distinct keys, which is not necessarily the case for the table-per-class strategy.
The single-table and joined inheritance strategies facilitate relationships involving the abstract superclass; these are not well supported by the table-per-class strategy.
Support for the table-per-class strategy is optional. JPA providers are not required to support it, and the default provider in the GlassFish reference implementation in fact does not support it. Applications that rely on table-per-class therefore are not guaranteed to be portable. (Your provider, Hibernate, does support it.)
You go on to say,
However, in this case I get java.sql.SQLSyntaxErrorException:
ORA-00904: "KEY": invalid identifier. Seems like using #Inheritance
together with #AttributeOverride for #Id field doesn't work.
#AttributeOverride is only specified to work for overriding the attributes of mapped superclasses and fields and properties of embedded classes. It does work for #Id properties if they appear in those contexts. It is not specified to work (though neither is it specified to not work) for persistent fields and properties inherited from an entity superclass, but do observe that it cannot work for such properties with either the single-table or the joined inheritance strategy.
If #AttributeOverride did happen to work for you, that use would be non-portable. On the other hand, JPA has nothing else to accomplish what you want. A particular persistence provider could have an extension that supports it, but Hibernate has not historically done so -- all properties inherited from an entity superclass are mapped with the same names.
You also say,
One more thing I'd like to mention is that I've tried using
#MappedSuperclass instead of #Inheritance but in this case I'm not
able to query with abstact Vehicle type.
JPA does not provide a solution for your particular combination of requirements:
Mapping each concrete entity class to a separate table,
Naming the ID to a different column name in each entity table, and
Supporting polymorphic queries on the abstract supertype.
If you are unwilling to change any of those then you'll have to rely on an extension. And in that case you're in luck: Hibernate supports polymorphic queries where the polymorphic type is not mapped as an entity. Thus, if you're willing to make your application explicitly dependent on Hibernate, you can probably get where you want to be.
Specifically, to do this in Hibernate you would rely on "implicit polymorphism". To do this, you would avoid mapping the superclass as an entity, and from your experience, I guess it should not be a mapped superclass, either. It can be an ordinary class, though its properties would not be persistent, or you could use an interface instead. If your Vehicle class has properties that you want to make persistent, then you could change it to an embeddable class. You would furthermore annotate each of the vehicle entities to specify implicit polymorphism, for example:
#Entity
#Polymorphism(type = PolymorphismType.IMPLICIT)
// ...
public class Car implements Vehicle {
// ...
}
The Hibernate docs claim that implicit polymorphism is the default, but I recommend applying the #Polymorphism annotation anyway, for clarity.
A MappedSuperclass uses inheritance for field and code reuse.
In addition we leverage some composition via Embeddable feature provided Hibernate
#Embeddable
public class Department {
private long deptId;
private String name;
private String description;
}
#Entity
#Table(name="CSE_DEPT", schema="test")
public class CSEDepartment{
#Embedded
private Department dept;
}
What is difference between #MappedSuperclass and #Embeddable and what is appropriate situation where they use.
#MappedSuperClass is good when we want to share some state between different entities so they will have the same fields and you can reuse the fields in your different entities. Generally it is created as a abstract class and you cannot create its own instance. You use this when you want to mimic the 'Is A' relationship. It cannot be used as a target for associations.
#Embeddable is used to map composite value types. #Embeddable class is used as one of the field type with in our entities. you can use this when you want to mimic 'Has A' relationship.
The case you have stated in the question, it may not be suitable for any. because both MappedSuperClass and Embeddable cannot be an entity..(you have departmentid in the department class which i am assuming should be a primary key).
For your case better is to use #OneToOne entity association because cse department will have a uniqu id name and desc. you dont want to have 2 csedepartment in one college.
I have a base (abstract) class A and two subclasses B and C which I want to persist them using table-per-hierarchy approach in Hibernate. Suppose B has 'width' and C has 'height' field which are of the same type. By default Hibernate creates two separate columns but fills only one column for each row. Now, what happens if I map both fields to the same column in DB, say 'length'?
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="type", discriminatorType=DiscriminatorType.INTEGER)
public abstract class A {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer id;
}
#Entity
#DiscriminatorValue("1")
public class B extends A {
#Column(name="length")
public Integer width;
}
#Entity
#DiscriminatorValue("2")
public class C extends A {
#Column(name="length")
public Integer height;
}
I ran some basic tests and no error happened, but I didn't see anything like this in documentations -considering this practice prevents creation of lots of empty columns in a complex application and should be mentioned - and really don't have the expertise to declare this as a safe practice.
Has anyone done something like this? Are there any drawbacks to this approach? Also does the no-NOT-NULL-on-subclasses limitation holds in this scenario?
I have not encountered any problems after using this method for two years in production.
But notice that you cannot set different restrictions on fields because database sees them as one column.
I have some philosophical intuitive feeling that adding fields which doesn't mapped to the DB corrupts entity classes and is a wrong way of solving problems.
But are there any concrete situations where using #Transient fields leads to implicit and hard fixing problems?
For example, is it possible that adding or removing 2nd level cache will break our app when there are #Transient fields in our entities?
Considerable update: after some thinking on #Transient fields it seems to me that #Transient fields just should be used in a proper way.
By 'proper way' I mean that entity always should have same behavior. It means that it's a very error-prone behavior when getters returns null's from time to time depending on #Transient field value. And it means that #Transient fields should always be initialized.
And I see only 2 cases of proper usage:
#Transient fields should be initialized in object's constructor:
#Entity
public class SomeEntity
#Id
private long id;
#Transient
private String transientField;
public SomeEntity () {
transientField = "some string";
}
...
}
#Transient fields can be lazy initialized:
#Entity
public class SomeEntity
#Id
private long id;
#Transient
private String transientField;
public String getTransientField () {
synchronized (lock) {
if (transientField == null) {
transientField = "some string";
}
}
return transientField;
}
...
}
Can anyone coment these 2 cases or describe other cases which I missed?
I am using the Transient annotation in some projects that persist with hibernate as well and didn't have any problems yet.
It is usually used for fields that can be determined by other persistent properties and using a cache should work also, because Javas Serialization mechanisms (caches usually expect the cached objects to be serializable) take the Transient annotation into consideration, too. I think it is preferrable to use transient getter and setter properties that provide the information instead of instance fields whenever possible.