JPA: single table inheritance possible performance tradeoff - sql

I have following MS SQL DB tables, where I am storing school related data:
HighSchools<---GradeLevels<---Courses<---CourseGrades
The relations between the tables looks like
#Entity
#Table(name = "GradeLevels", schema = "dbo")
public class HighSchoolGradeLevel {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "AttendedId", nullable = false)
private HighSchoolAttended highSchoolAttended;
#OneToMany(mappedBy = "highSchoolGradeLevel")
#OrderBy("createdDate ASC")
private List<HighSchoolCourse> highSchoolCourses = new ArrayList<>(0);
// other fields and getter/setter methods....
}
#Entity
#Table(name = "Courses", schema = "dbo")
public class HighSchoolCourse {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "GradeLevelId", nullable = false)
private HighSchoolGradeLevel highSchoolGradeLevel;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "highSchoolCourse", cascade={CascadeType.REMOVE, CascadeType.PERSIST})
#OrderBy("createdDate ASC")
private List<HighSchoolCourseGrade> highSchoolCourseGrades = new ArrayList<>(0);
// other fields and getter/setter methods....
}
#Entity
#Table(name = "CourseGrades", schema = "dbo")
public class HighSchoolCourseGrade {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CourseId", nullable = false)
private HighSchoolCourse highSchoolCourse;
// other fields and getter/setter methods....
}
The java web-app users can do CRUD operations with the tables data.
The new requirement is to consume the data with the same structure from a third party. The consumed data SHOULD NOT be displayed for the web-app users.
Therefore, I am thinking to use JPA single table inheritance which allows me to:
avoid adding clones of the existing tables
avoid cloning existing business logic which is tight to existing
entity types
avoid code refactoring of existing CRUD ops in case implementing own discriminator column.
The Single table inheritance looks reasonable to use in my, but there is one thing I am not sure about "What is the performance trade off of using Single Table Inheritance, especially while operating on the Parent and its Childs entities?". The question may sound dummy, knowing the fact that Hibernate will update SQL queries with corresponding discriminator statements. But still it would be nice to improve the current knowledge and be sure that the existing users of wep-app will not get bad usage experience.
The existing web-app DAO layer uses with Spring Data JPA. Currently, each of existing tables contains more than ~500k records.

Related

Optaplanner: problems with InverseRelationShadowVariable

have a many-1 relationship pupil-formGroup: pupils are assigned to a formGroup and a formGroup can contain many pupils. I have attempted to implement an InverseRelationShadowVariable having watched your video/tutorial https://www.youtube.com/watch?v=ENKHGBMDaCM (which does not quite correspond with the latest optaplanner documentation I realise)
FormGroup extracts
#Entity
#PlanningEntity
public class FormGroup {
#InverseRelationShadowVariable(sourceVariableName = "formGroup")
#OneToMany(mappedBy = "formGroup", fetch = FetchType.EAGER)
private List<Pupil> pupilList = new ArrayList<Pupil>();
public List<Pupil> getPupilList() {
return pupilList;
}
public Integer getPupilCount() {
return pupilList.size();
}
Pupil extracts
#Entity
#PlanningEntity
public class Pupil
#PlanningVariable(valueRangeProviderRefs = "formGroupRange")
#ManyToOne
private FormGroup formGroup;
Config extracts
<solutionClass>org.acme.optaplanner.domain.Plan</solutionClass>
<entityClass>org.acme.optaplanner.domain.Pupil</entityClass>
<entityClass>org.acme.optaplanner.domain.FormGroup</entityClass>
I believe I've followed the steps in the videoexactly (don't we all say that) but at solve time I get hundreds of errors... Repetitions of the following
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
Any hint gratefully received...
The InverseRelationShadowVariable creates a bi-directional relationship between the genuine planning entity (Pupil) and the planning value (FormGroup). This may become problematic if you re-use your planning domain classes for other purposes, such as ORM persistence or serialization.
In this case, Jackson is unable to serialize Pupil, because it references a FormGroup, which has a List containing a reference back to that Pupil. See? An endless loop.
Solve this issue by adding the #JsonIgnore annotation on top of your inverse relation property and breaking that loop for Jackson:
#Entity
#PlanningEntity
public class FormGroup {
#JsonIgnore // THIS IS THE FIX
#InverseRelationShadowVariable(sourceVariableName = "formGroup")
#OneToMany(mappedBy = "formGroup", fetch = FetchType.EAGER)
private List<Pupil> pupilList = new ArrayList<Pupil>();
public List<Pupil> getPupilList() {
return pupilList;
}
public Integer getPupilCount() {
return pupilList.size();
}
...

Attempting achieve low coupling between JPA entities

One issue I'm running into is a good way to map One-To-Many relationships with JPA / Hibernate, without sacrificing SOLID principles along the way. Here's an example of this problem from a current project I'm working on:
Given the following code which defines a unidirectional One-To-Many relationship:
#Entity
#Table(name = "users")
public class User extends AggregateEntity<User> implements HasRoles, HasContactInfo, HasContacts, HasDeals {
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_hasContacts")
private Set<Contact> contacts;}
My understanding is that this creates an "fk_hasContacts" column in the "contacts" table, without the "Contact" class knowing or caring which object it is being referenced from. Also, notice how "User" implements the "hasContacts" interface, which creates a decoupling effect between the two entities. If tomorrow I want to add another class; say "BusinessEntity" which also has contacts, there's nothing that needs to be changed within the current code. However, after reading up on the topic in seems that this approach is inefficient from a database performance perspective.
The best way to map a #OneToMany association is to rely on the #ManyToOne side to propagate all entity state changes - As expounded upon here.
Seems to be the prevailing wisdom. Were I to engineer it that way, the "User" class would now look like this:
#Entity
#Table(name = "users")
public class User extends AggregateEntity<User> implements HasRoles, HasContactInfo, HasContacts, HasDeals {
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Contact> contacts;
And the (previously decoupled) "Contact" class would now have to look like this:
#Entity
#Table(name = "contacts")
public class Contact extends StandardEntity<Contact> {
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "fk_user")
private User user;
The key problem is that it seems from here that JPA doesn't support defining an interface as an entity attribute . So this code:
#Entity
#Table(name = "contacts")
public class Contact extends StandardEntity<Contact> {
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "fk_user")
private HasContacts hasContacts;//some class which implements the "HasUser" interface
Isn't an option. Seems one would have to chose between SOLID OOP, and valid ORM entities. After a lot of reading I still don't know whether the hit to database performance is bad enough to justify writing what amounts to tightly coupled, rigid and ultimately bad code.
Are there any workarounds / solutions to this seeming contradiction in design principles?

Spring Data JPA. Choose all posts that contain tags

I use Spring Data. I have following entities:
#Entity
class Post {
#Id
private Long id;
private String message;
#ManyToMany
#JoinTable(name = "POST_TAG", joinColumns = {
#JoinColumn(name = "POST_ID", referencedColumnName = "ID"),
#JoinColumn(name = "TAG_ID", referencedColumnName = "ID")
})
private Set<Tag> tags;
}
#Entity
class Tag {
#Id
private Long id;
private String name;
}
In PostRepository I want to have 2 methods, which:
find all posts that contain at least one (or more) of specified tags.
find all posts that contain all specified tags.
Something like:
List<Post> findByTagsContainsAnyOf(List<String> tags);
List<Post> findByTagsContainsAll(List<String> tags);
The first one is easy - List<Post> findByTagsIn(List<Tag> tags). Note that it is List<Tag> instead of List<String> since it is Tag that is mapped to Post and not String. If you want to stick to List<String>, you will have to use a #Query. Something like #Query("SELECT p FROM Post p WHERE p.tags CONTAINS (SELECT t FROM Tag t WHERE t.name IN :tags)") should work.
The second one will have to be done through a #Query by putting a join between the Post and Tag entities.
In general, JPA is merely an object-oriented API over SQL. So, when in doubt, create the SQL and then convert it into the equivalent JPQL. Where the JPQL is straightforward, it can be coded as the method name, relying on the Spring Data query creation framework to generate the JPQL from the method name.

Hibernate inheritance without table for basic entity

I've never used inheritance in hibernate and I don't know which strategy should I use (or even do I really need to use strategy). I have three tables with the same interface (the same columns) and I want to create three entities with basic interface for them so it will look like this:
#Entity
+ Basic
+ #Entity
#Table(name="TABLE_1")
Table1
+ #Entity
#Table(name="TABLE_2")
Table2
+ #Entity
#Table(name="TABLE_3")
Table3
As you see I don't want to use table for basic entity. If it is possible to do this kind of inheritance, how to do it? Maybe I don't need 'hibernate' inheritance and I should use normal inheritance?
In application it is used like this:
Somewhere in configuration we store information which entity to use (Table1, Table12 or Table3)
Choosen entity is used in our queries (some writen in HQL, some in Criteria) so each query should know which entity to use.
EDIT
What's more each entity can be used as attribute of some entities and we wan't to know which table should be used. For example:
#Entity
#Table(name="USER")
class User {
#Id
private Integer id;
#ManyToOne
#JoinColumn(name = "SOME_ID", referencedColumnName = "ID", nullable = false)
private Basic basicEntity; // how to use proper strategy using some configuration value (eg. class static attribute or configuration value stored in db?)
}
I think this is recommended way of achieving your goal:
#MappedSuperclass
public abstract class BaseEntity {
public static final int SHARED_PAREMETER = 2;
#Column(name = "modified", columnDefinition = "TIMESTAMP DEFAULT NOW()")
protected Date modified;
//... other fields, getters and setters
}
#Entity
#Table(name = "TABLE_1")
public class Table1 {
#Id
private Integer id;
#ManyToOne
#JoinColumn(name = "SOME_ID", referencedColumnName = "ID", nullable = false)
private Table2 table2;
}
#Entity
#Table(name = "TABLE_2")
public class Table2 {
#Id
private Integer id;
}
In this case, we will have only two tables but both would have fields from BaseEntity. You can't, though, make a relation in Entity to an abstract class but in processing you're fully entitled to do something like this:
public void process(BaseEntity entity){
// processing..
}

How to fetch data from two tables(one-to-many relations) using Hibernate Createcriteria

How to fetch data from two tables based upon User id ?
*****************Role Class***********************
#Entity
#Table(name = "IUC_CON_USER_ROLE_MAP")
public class Role {
#Id
#Column(name="F_ROLE_MAP_ID")
int rolemap;
#Column(name="F_ROLE_ID")
int roleid;
#OneToMany(mappedBy="role",fetch=FetchType.EAGER)
Set<User> F_USER_ID;
*********************User class*******************
#Entity
#Table(name = "IUC_CON_USER")
public class User implements Serializable {
#Id
#Column(name = "F_USER_ID")
private int id;
#Column(name = "F_USER_NAME")
private String name;
#Column(name = "F_USER_PWD")
private String pwd;
#ManyToOne
#JoinColumn(name="F_USER_ID",insertable=false,updatable=false)
private Role role;
----------setter and getter for properties
}
External edit:
Criteria creation code:
DetachedCriteria uCrit = DetachedCriteria.forClass(User.class, "user");
uCrit.add(Restrictions.eq("user.id", 5));
uCrit.setProjection(Projections.property("user.id"));
DetachedCriteria criteria = DetachedCriteria.forClass(Role.class, "role");
criteria.add(Property.forName("role.F_USER_ID").in(uCrit));
List lt1 = criteria.getExecutableCriteria(getSession()).list();
use createAlias
criteria.createAlias("propertiy_of_main_entity", "aliastName");
For me this fetches entities asociated with root entity via some entity property name;
in your case something like
createAlias("F_USER_ID", "roleUsers");
should do.
Anyway why ins't your field name following commong naming doctrine for Java? WHY_IS_IT_UPPERCASED_WITH_DASHES_LIKE_CONSTANTS_?
All in all everything is explained in Hibernate documentation. If alias won't work, than fetch associations like in examples from Hibernate documentation under link I provided.
You can use below series of statements to fetch User along with the Role:
int userId = 1;
Criteria criteria = session.createCriteria(User.class);
criteria.setFetchMode("role", FetchMode.JOIN);
criteria.add(Restrictions.eq("id", userId));
User user = (User) criteria.uniqueResult();
The default fetching strategy of Hibernate is to fetch the associations lazily, which you need to override at runtime in code, if you want the associations (here the Role) to be fetched along with User. The third statement is doing this overriding by seting the fetch mode to JOIN. This statement FetchMode.JOIN overrides the default behavior, so that Role will be fetched along with the User.