I am using Play Framework 1.2.4 with PostgreSQL and JPA. I would like to have a Model hierarchy and see that there are some alternatives to doing this.
I have a base class (which is abstract) and two concrete classes extending this base class. I don't want to persist this base class while I want to have concrete classes. In the base class, I have another Model classes as properties, in other words, I have #ManyToOne relationships in my base class.
My question is what is the best way of implementing this? Using #MappedSuperclass or #Inheritance with TABLE_PER_CLASS strategy? I am a bit confused as they seem virtually equivalent.
I also have some concerns about querying and performance issues that I might face in future.
MappedSuperClass must be used to inherit properties, associations, and methods.
Entity inheritance must be used when you have an entity, and several sub-entities.
You can tell if you need one or the other by answering this questions: is there some other entity in the model which could have an association with the base class?
If yes, then the base class is in fact an entity, and you should use entity inheritance. If no, then the base class is in fact a class that contains attributes and methods that are common to several unrelated entities, and you should use a mapped superclass.
For example:
You can have several kinds of messages: SMS messages, email messages, or phone messages. And a person has a list of messages. You can also have a reminder linked to a message, regardless of the kind of message. In this case, Message is clearly an entity, and entity inheritance must be used.
All your domain objects could have a creation date, modification date and ID, and you could thus make them inherit from a base AbstractDomainObject class. But no entity will ever have an association to an AbstractDomainObject. It will always be an association to a more specific entity: Customer, Company, whatever. In this case, it makes sense to use a MappedSuperClass.
#MappedSupperclass is different than the #Inheritance annotation.
#MappedSuperclass tells the JPA provider to include the base class persistent properties as if they were declared by the child class extending the superclass annotated with #MappedSuperclass.
However, the inheritance is only visible in the OOP world, since, from a database perspective, there's no indication of the base class. Only the child class entity will have an associated mapped table.
The #Inheritance annotation is meant to materialize the OOP inheritance model in the database table structure. More, you can query a base class annotated with #Inheritance but you can't do that for a base class annotated with #MappedSuperclass.
Now, the reason why you'd want to use the #Inheritance JPA annotation is to implement behavior-driven patterns like the Strategy Pattern.
On the other hand, #MappedSuperclass is just a way to reuse both basic properties, associations, and even the entity #Id using a common base class. Nevertheless, you can achieve almost the same goal using an #Embeddable type. The only major difference is that you can't reuse an #Id definition with #Embeddable, but you can do it with #MappedSuperclass.
Related
I am trying to design a database schema so that it is applicable for the Hibernate's Table Per Class inheritance strategy. So, I will not let Hibernate to generate the tables, instead I need to design them myself in Liqibase but in such way that Hibernate can use them with that strategy.
My entity classes should look like this.
Vehicle.java
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Vehicle {
#Id #GeneratedValue
private int id;
private String name;
private String brand;
}
Car.java
#Entity
public class Car extends Vehicle {
private String oil;
}
Bike.java
#Entity
public class Bike extends Vehicle {
private String frame;
}
Now, I know that that this strategy means that all the fields are in all tables, but I am not sure about two things.
1) Do I need to include the ID in the derived tables?
2) Do I even need a Vehicle table in the DB, since it will actually never be used as an entity (rather just an abstract class)?
I'd appreciate if someone could clear that for me.
Now, I know that [the table-per-concrete-class] strategy means that all the fields are in
all tables,
Well no, you seem to be confused. With InheritanceType.TABLE_PER_CLASS, each concrete entity class E maps to a table that contains columns corresponding to all the fields of E, including inherited ones, but not including fields of entities that are not superclasses of E.
This is to be contrasted with InheritanceType.SINGLE_TABLE, in which all entity types in a whole inheritance hierarchy map to the same table, which then necessarily contains a column for each property of each entity in the hierarchy (without duplicating inherited fields).
Note also that it is odd that your Vehicle entity contains fields with the same names as fields of its subclasses. Java fields are not polymorphic, so this is unlikely to be what you want. It's certainly unnecessary.
but I am not sure about two things.
1) Do I need to include the ID in the derived tables?
Supposing that you stick with TABLE_PER_CLASS, yes, each entity table needs to provide a column for each persistent property of the corresponding entity class, including those inherited from superclasses. That includes id.
2) Do I even need a Vehicle table in the DB, since it will actually
never be used as an entity (rather just an abstract class)?
Not if you actually declare it abstract, but if you leave it concrete, then yes, you need a table for it. And that makes sense, because in that case you could have Vehicle entities that are neither Cars nor Bikes.
Hibernate schema generation tool should be able to generate your schema if you annotate your entities correctly.
In this case Vehicle table which be generated since your entity class is concrete. You need to define the class as abstract.
All the fields defined in the superclass (including the #Id) will be copied in the corresponding child class tables. There is a limitation though regarding id auto generation. You cannot use the table per class inheritance strategy with the GenerationType.IDENTITY strategy since the generated keys should be unique across all tables. Identity creates unique ids per table
Make sure the Generation.AUTO strategy does not map to the IDENTITY strategy for your RDBMS (You can specify a strategy explicitly eg GenerationType.TABLE to override the default)
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.
I am trying to develop an application with Hibernate and javax.persistence Annotations. I have some troubles in mapping an interface.
I have mapped my interface as #MappedSuperclass and I have already tried to use the attribute targetEntity in my ManyToOne Annotation that referred to the interface and it worked.
The problem is that I would more than one class to act as targetEntity. In particular I'd like that every class that implements my interface, could be recognized as a target entity.
Does anybody knows if it's possible to do it?
I think this
In particular I'd like that every class that implements my interface,
could be recognized as a target entity.
is not possible. Only classes which are annotated with #Entity or which are mapped in an other way (for example with a mapping file) can be persisted.
Also, the Hibernate documentation says:
Note
Annotating interfaces is currently not supported.
See the rather old question Interfaces with hibernate annotations for additional information.
I have some Entities that look like this:
#Entity
public abstract class AbstractEntity{
#Id
private Long id;
String name;
User author;
#Lob
byte[] data;
}
public class EntityOne extends AbstractEntity{
// nothing new
}
public class EntityTwo extends AbstractEntity{
// nothing new
}
public class EntityThree extends AbstractEntity{
// nothing new
}
The byte[] defined in the superclass is the interesting part of each subclass entity. The reason I want to subclass is to enforce typing rules, as some parts of the program require EntityThree, while some parts of the program require EntityTwo. the byte[] will have completely different meaning from subclass to subclass.
It feels a little strange, because I don't expect that any of the subclasses will add any fields or methods... So i use inheritance only as a typing tool.
Is this a reasonable design? A better way to accomplish this? Does it break any best practices?
It is a good OO practice. For the database, use the SINGLE_TABLE Inheritance strategy (the default), and maybe use a custom #DiscriminatorValue per subclass, otherwise you'll get classnames in your table.
From a DBA perspective, there is no difference between this and using a single class with a property TYPE.
For OO, the subclasses can be much more usefull, e.g. you can use it to implement certain methods differently or implement the visitor or strategy pattern instead of tedious if-then-else or instanceof structures.
It's hard to say without knowing the exact use-case, but it looks like you just want to reuse the common fields in all the entities, but that no other entity will have references to AbstractEntity - only to one specific subclass. In this case, you should map the superclass with #MappedSuperclass rather than with #Entity. Each entity will have its own table, and can override the mapping defined in the mapped superclass.
Note that if I'm mistaken, and you really need an entity inheritance, using instanceof, at least with Hibernate, will cause problems, because you might have a proxy to the base class, which doesn't know yet the exact subclass the foreign key is pointing to, and which is thus not an instance of any of the subclasses.
I have about 10 different entities in my J2EE application that right now share the exact same implementation. They all inherit from the same generic abstract class that has been annotated as a #MappedSuperclass, but this class contains none of the implementation I have repeated in all the concrete subclasses.
If I could, I'd put all the various fields and collections on this abstract superclass and therefore put the implementation methods there too -- all in one place instead of 10. However, due to JPA restrictions I cannot add JPA annotations to generic fields or accessors.
While I normally favor delegation to implementation inheritance anyway, due to another JPA restriction that says you can't have an embedded entity with a collection the idea of using a delegate also won't work.
When I had only 3-4 of these entities and 2-3 methods, it wasn't a big deal, but now I have about 10 -- and about 7-8 methods each...and some of the methods are getting really complex. And the "cut-copy-paste" inheritance I am using really sucks.
Any other brilliant ideas out there?
Double-check whether these "restrictions" actually hold true for your JPA provider. I've had embedded objects with collections and it has been fine (with Hibernate). And I've had a #MappedSuperclass with mapped fields.
You can try omitting the #MappedSuperclass, and make the superclass abstract and an #Entity with the proper inheritance hierarchy.
It turns out you can use either implementation inheritance or delegation as long as you are sure to use property access mode for your JPA annotations. I was using field access mode for JPA annotations and that was causing me no end of suffering because I could not annotate a generic field type.
However with property access mode I simply create my generic abstract implementation without annotating it as an entity, mappedsuperclass, embeddable, or anything. This way JPA will ignore it. Then, in the concrete subclasses, I create protected getter and setters methods as needed and put the JPA annotation on those.
Turned out to be deceptively simple.