JPA entity inheritance without additional tables - java

I am working on a Maven Java web project based on Spring, JPA and Hibernate.
One part of this project shall be reused in another very similar project and gets therefore extracted as Maven module. This service and the corresponding entity are annotated like this:
#Service
public class MessageService
#Entity
public class Message
Both projects have similar but slightly different UserEntities.
#Entity
public class TypeAUser
#Entity
public class TypeBUser
The Message Entity has #OneToMany relationship to one of the UserEntities in each project.
I thought about a generic UserEntity but want to avoid creating additional tables as well as tables with fields of the "other" project.
Any ideas are appreciated.
Best regards,
Stefan

If you don't want to create additional tables, then you might want to consider using SINGLE_TABLE strategy.
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class UserEntity {
...
}
#Entity
public class TypeAUser extends UserEntity {
...
}
#Entity
public class TypeBUser extends UserEntity {
...
}

Well, when talking about Inheritance in Hibernate, you have three options:
Table per concrete class with unions
Table per class hierarchy(Single Table Strategy)
Table per subclass
Since you want to achieve it with one table, I suggest using option 2.
You need just a User table, with a USER_TYPE column to discriminate the user types ( Can be a number, varchar2, etc)
Then you need to create a class User with the following annotations:
You can specify a DiscriminatorColumn if you want, otherwise Hibernate will default to '{className}_TYPE
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="USER_TYPE", discriminatorType=DiscriminatorType.STRING)
public class User { .... }
In your concrete class implementations you can specify a Discriminator value (or if you don't, hibernate will default it to the class name ('TypeAUser'/ 'TypeBUser')
#Entity
#DiscriminatorValue('A')
public class TypeAUser
#Entity
#DiscriminatorValue('B')
public class TypeBUser

Related

Mixing Hibernate Inheritance Strategies

In C# we are using an ORM that lets us specify in each child/Sub Class where we want to store it:
[MapInheritance(MapInheritanceType.ParentTable)] //this means that store employee specific fields in person table
public partial class Employee: Person
{}
I want to use the same in Java side,But in Hibernate we specify strategy in parent Class .We have following structure: enter image description here
I don't want a table for base class.I'd like to store person & employee in user table. and customer in its own table.
But it seems that hibernate has shortcoming in this regard and asks for one hierarchy policy for all branch. I want to be able to change policy in lower branches.
How is it possible to implement this in jpa or hibernate?
You can do that using #MappedSuperclass
One #MappedSuperclass or multiple #MappedSuperclass are allowed in same inheritance hierarchy.
#MappedSuperclass
public class FirstMapped {
#Id int id;
}
#MappedSuperclass
public class SecondMapped extends FirstMapped {
String createdOn;
}
#Entity
public class MyTable extends SecondMapped {
String myColumn;
}
These classes must create just one table: MyTable with columns:
id
createdOn
myColumn

What is the difference between #Entity and #Repository annotations?

I am new in spring and hibernate and I got these two annotations #Entity and #Repository used for DAO class. As both the annotations are used for the DAO class. Then, what is the difference and when to use one of them.
The #Entity class is the model and the #Repository is the layer that helps you to extract the data from database. For example :
#Entity
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(unique=true)
private String name;
//getters - setters
}
And the repository:
#Repository
public interface StudentRepository extends CrudRepository<Student,Long> {
public Student findByName(String name);
}
The basic CRUD operations are already provided by CrudRepository interface so there is no need to implement them again. You can use them in a Service class like this:
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
public StudentRepository studentRepository;
#Override
public List<Student> findAll()
{
return studentRepository.findAll():
}
#Override
public Student findByName(String name)
{
return studentRepository.findByName(name);
}
}
And in case you want to make custom queries like get a student by name, jpa hibernate is very smart and helps you to only define the method in the #Repository annotated interface and there is no need of any implementations. BUT there is a rule here if you want to make it work. Hibernate will look after method name like this : public Student findByName(String name); the find and Student return type tells hibernate that it have to look for a Student, byName will tell that it have to query the database for a Student with a specific name. (The Name keyword is actually the entity attribute with capital letter ! )
But of course, if you need some more complex queries, there is the #Query annotation that will help you with that :) .
#Entity annotation defines that a class can be mapped to a table, it is just a marker, like for example Serializable interface.
Entity is an object representing (usually) a row in a db.
#Repository annotation defines CRUD operation on table.
It is very like DAO pattern to fetch and save entities from/to storage - it represents db table.
#Entity
Let's say we have a POJO called Student which represents the data of a student and we would like to store it in the database.
public class Student {
// fields, getters and setters
}
In order to do this, we should define an entity so that JPA is aware of it.
So let's define it by making use of the #Entity annotation. We must specify this annotation at the class level.
#Entity
public class Student {
// fields, getters and setters
}
In most typical applications, we have distinct layers like data access, presentation, service, business, etc.
And, in each layer, we have various beans. Simply put, to detect them automatically, Spring uses classpath scanning annotations.
#Repository
#Repository annotates classes at the persistence layer, which will act as a database repository. #Repository’s job is to catch persistence specific exceptions and rethrow them as one of Spring’s unified unchecked exception.
to sum up #Entity is part of JPA Java Persistence API specification used mapping between a java POJO and an entity in relational database world and #Repository is a Spring stereotype used to annotate POJO beans than their jobs is database manipulation operations

Choosing Inheritance Strategy - Hibernate

if this question was aksed here, i surely couldnt find it, or it didnt particulary help me.
i've read some tutorials and some questions for Inheritance Mapping, which couldnt quitely solve my questions.
Say i have an abstract class :
User
And 3 more other subclasses :
UserA, UserB, UserC
those all extend User.
Every Subclass has its own table, Superclass User, meanwhile, doesn't.
In my other class Website i have a ArrayList, or should i say Collections, of Users.
The list should fetch all users of the Website.
Which strategy should i use ? I thought of MappedSuperclass, but since in my Website class the List is of User type, so am not sure what to do here.
Thanks for any help!
With JPA the Java implementation always depends on you own preferences and requirements, sometimes it is the matter of a choice.
Yes, #MappedSuperclass will do.
You can have every child with unidirectional relationship to Website. Then you gonna have Website object inside your User class (with a bunch of annotations), which will map to a database as foreign_key field (presume you are using SQL storage and 'Repositories' DAO abstraction from JPA).
It is not necessary to store a collection of users inside Website class. Just think if you really need it - it can be a mess to support consistency.
But there are cases where you need bidirectional relationship. When you store objects in memory (for caching purposes for example) you'll probably need to have this collection. In this case why not to have 'User' collection? You will fetch data through dedicated repositories(or even if you're not using those, any other way will be using 'User' tables with foreign_key, not the 'Website' table) anyway.
So, for example with the use of Spring Data JPA you can define a unidirectional relationship in a superclass and use 'repositories' next way(and bidirectional example you can find anywhere in the internet, so I am not providing it):
#Entity
public class SuperUser extends User {
...
}
#Entity
public class BasicUser extends User {
...
}
#MappedSuperclass
public abstract class User implements Serializable {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "website_uuid", nullable = false)
protected Website website;
...
}
#Entity
public class Website implements Serializable {
...
}
#Repository
public interface SuperUserRepository extends CrudRepository<SuperUser, Long> {
Iterable<SuperUser> findByWebsite(Website website);
}
#Repository
public interface BasicUserRepository extends CrudRepository<BasicUser, Long> {
Iterable<BasicUser> findByWebsite(Website website);
}
What you are asking for seems a typical "Table-per-concrete-class" inheritance strategy. https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#entity-inheritance-table-per-class
In older version of the user guide, it has mentioned that separate table will be mapped for each non-abstract classes. In the latest document the "non-abstract" part is not mentioned but I believe it still works similarly.
So it looks something like:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class User {...}
#Entity
class UserA extends User {...}
#Entity
class UserB extends User {...}
#Entity
class UserC extends User {...}
But you should be aware of this inheritance strategy usually gives inefficient query as internally it is using union.

JPA - how to model this join with conditions?

I was trying to find an answer but, unfortunately, with no luck.
The data structure looks like this:
TABLE_X - contains userId, also userType telling if this is external or internal user
INTERNAL_USERS - contains key userId
EXTERNAL_USERS - also contains key userId
TABLE_X.userId is either INTERNAL_USERS.userId or EXTERNAL_USERS.userId.
Now, I would like to map an entity out of TABLE_X and have user object mapped to correct entity, either INTERNAL_USERS or EXTERNAL_USERS.
How should I do this?
Should I create two fields and map one to INTERNAL_USERS and one two EXTERNAL_USERS and just see which one is not empty?
If I understand correctly your question, what you have to do is to replicate structure of the TABLE_X columns with fields on the TABLE_X class, and add to fields one for INTERNAL_USERS.userID and one for EXTERNAL_USERS.userID
But if you store on TABLE_X.userType if a user is internal or external, I think that the best thing you can do is not create any other table, because you just have the information you need on your first table (TABLE_X). If you want to know all the users that are internal for instance, just do a SQL and select all the values from TABLE_X where userType = "Internal"
Use Hibernate inheritance. Check out the Table per class inheritance pattern. Here you have both INTERNAL_USERS or EXTERNAL_USERS data in TABLE_X with userType as the discriminator column.
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e1191
Given the table structure, you can use JPA Inheritance as detailed here:
https://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Example_joined_inheritance_annotations
In Hibernate you can model such relationships as follows. When querying for a User you can then rely on Hibernate to return an instance of the correct type.
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class User{
#Id
private Long userId;
}
#Entity
public class InternalUser extends User{
}
#Entity
public class ExternalUser extends User{
}
As noted in the article linked to, Hibernate does not require a discriminator column to be specified when using joined inheritance. It does however support a discriminator column if one is available. As you have one available in your schema - userType - then you could explicitly specify it if you wish. I imagine this would yield some performance benefit in terms of the generated SQL:
Mappings with optional discriminator column:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="userType")
public class User{
#Id
private Long userId;
}
#Entity
#DiscriminatorValue("INT")
public class InternalUser extends User{
}
#Entity
#DiscriminatorValue("EXT")
public class ExternalUser extends User{
}

Any way to get fields from entity without actually joining into that table?

I'm not sure this is possible, but knowing just the very basics of JPA, I want to ask if it is possible. Basically I have an entity (We'll call it MyEntity) with a bunch of fields on it. We now want a 2nd entity that has all the same fields as MyEntity plus some of it's own. The use case for this is archiving these entities. We want to store all the archived entities in a separate table than MyEntity so that we don't have to qualify all the queries with archived=false. The JPA annotations for MyEntity look something like this:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class MyEntity
{
....
There are multiple classes that extend this abstact class, each with #DiscriminatorValue annotations
For my archived entity (MyArchivedEntity) I want something along the lines of this:
#Entity
public class MyArchivedEntity
{
private MyEntity entity;
private String archiveSpecificField;
....
The problem with this of course is that it will want to join into the MyEntity table and get a specifc MyEntity record for populate the entity field. Is there some kind of annotation or something I can do to just get the same fields/columns from that entity (MyEntity) into this entity (MyArchivedEntity)?
Like I said in the beginning, I'm not sure if this is possible, but I hope I've explained well enough the end goal of what I'm trying to achieve, so that there could be some way to achieve it. If it makes any difference, I'm using PostgreSQL with EclipseLink.
What you can do is using #MappedSuperclass on a AbstractParentEntity becoming the super class of both MyEntity and MyArchiveEntity. So you will have something like the following:
#MappedSuperclass
public abstract class AbstractParentEntity {
public String someField;
...
}
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class MyEntity extends AbstractParentEntity
{
//here you don't have any field (they all move to AbstractParentEntity
// (or, at least all the fields that need to be archivied are now declared in parent class)
....
}
#Entity
public class MyArchivedEntity extends AbstractParentEntity
{
private String archiveSpecificField;
....
More about MappedSuperclass here:
Mapped superclass inheritance allows inheritance to be used in the object model, when it does not exist in the data model. It is similar to table per class inheritance, but does not allow querying, persisting, or relationships to the superclass. Its' main purpose is to allow mappings information to be inherited by its' subclasses. The subclasses are responsible for defining the table, id and other information, and can modify any of the inherited mappings. A common usage of a mapped superclass is to define a common PersistentObject for your application to define common behavoir and mappings such as the id and version. A mapped superclass normally should be an abstract class. A mapped superclass is not an Entity but is instead defined though the #MappedSuperclass annotation or the <mapped-superclass> element.
You may wish to look into EclipseLink's history support. It can automatically maintain a historical archive table.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/History
Another option would be to map the same classes in another persistence unit using an orm.xml to the archive tables.

Categories

Resources