I'm trying to map a class hierarchy to a single table using Hibernate and one table not creating. I add #Inheritance(strategy=InheritanceType.SINGLE_TABLE) but in base there are separate tables.
#MappedSuperclass
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class User implements UserDetails {
#Id #GeneratedValue
private int id;
...
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
...
}
#Entity
public class Manager extends User{
...
}
#Entity
public class Administrator extends User{
...
}
Whats wrong?
Your User class should be
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(field = "type", discriminatorType = DiscriminatorType.STRING)
public class User implements UserDetails {
/* your original stuff */
}
Then you need to map the extended classes as follows:
#Entity
#DiscriminatorValue("manager")
public class Manager extends User { /* stuff */ }
#Entity
#DiscriminatorValue("administrator")
public class Administrator extends User { /* stuff */ }
This effectively should create a single table which houses all the fields from User, Manager, and Administrator using a special field that is added by Hibernate called type which will hold values of either manager or administrator.
Related
My domain contains FieldValidator objects, that have sub classes EmailValidator, SizeValidator etc. There is only one instance of FieldValidator per subclass.
#Entity
#Immutable
#Table(name = "FIELD_VALIDATOR")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE")
public abstract class FieldValidator extends AbstractEntity implements IFieldValidator{
#Column(unique=true, name="TYPE")
public String type;
public String name;
public String argumentName;
//To override by subclasses
public abstract Optional<CustomError> run(FieldConstraint constraint);
/**
* Constructor, getters and setters omitted
*/
}
Example subclass
#Entity
#DiscriminatorValue("DATE_BEFORE")
public class DateBeforeValidator extends FieldValidator {
public DateBeforeValidator() {
super("Date", "Before");
}
public Optional<CustomError> run(FieldConstraint constraint) {
//TODO
}
}
Example subclass
#Entity
#DiscriminatorValue("SIZE_MIN")
public class SizeMinValidator extends FieldValidator {
public SizeMinValidator() {
super("Size", "Minimum");
}
public Optional<CustomError> run(FieldConstraint constraint) {
//TODO
}
}
How can I initialise the FIELD_VALIDATOR table with one entity per FieldValidator subclass? I need ensure that: the FIELD_VALIDATOR table is immutable (no other data can be added or deleted to/from the table), each FieldValidator entity is unique and immutable (the #Column(unique=true) and the #Immutable annotations should do the trick).
I think your best bet is JPAs/Hibernates initial load feature. You can read about it here for example.
I have a few tables named MEMBER, PROVIDER, EMPLOYER etc and they have few common columns like ID, ADDRESS etc. I'm using hibernate to fetch data from these tables using Spring Data JPA. Since I have many similar tables, I created a super entity class and wrote a common method to fetch the data. Here is my super class.
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString
public abstract class Recipient implements Serializable {
#Id
#Column(name = "RECIPIENT_ID", unique = true, nullable = false)
private Long id;
}
Here is a sample entity which extends this super class.
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString
#Table(name = "MEMBER")
public class Member extends Recipient {
#Column(name = "Member_NAME")
private String membername;
}
Here is the Service
public class RecipientServiceImpl extends AbstractService<Recipient>
implements RecipientService {
#Override
public Recipient findByIdAndType(long id, String type) {
Recipient recipient = null;
switch (RecipientType.get(type)) {
case MEMBER:
recipient = recipientRepository.findMemberById(id);
case PROVIDER:
recipient = recipientRepository.findProviderById(id);
case EMPLOYER:
recipient = recipientRepository.findEmployerById(id);
}
}
And here is the repository
public interface RecipientRepository
extends JpaRepository<Recipient, Long>, JpaSpecificationExecutor<Recipient> {
public Member findMemberById(long id);
public Provider findProviderById(long id);
public Employer findEmployerById(long id);
}
Obviously it didn't work since there weren't any tables mapped in the Recipient abstract entity. Is there a way to implement what I was hoping for as a generic fetching method instead of having to create repositories for each entities ?
I inherited some pretty awful code that I am looking to refactor to make more reusable. There is a set of reporting tables which are primarily composed of 3 columns: id, report_type_fk, and report_description. I would like to merge all the reporting tables into one for ease of use.
I am refactoring the code and think that it would be better to break our current entities up so that Report is an abstract class with type implementations. For example a DmvReport extends Report, CreditScoreReport extends Report, etc.
The problem I am running into is that there would only be 1 report table that all entities would need to save to. Is there a way to make all concrete implementations of the abstract Report object save into the same table?
Here's an example of the bad code I inherited
Report class
#Entity
#Table(name = "report")
public class Report<E extends Exception> {
private long id;
private ReportType type;
private String description;
...
...
}
CreditReport class
#Entity
#Table(name = "credit_report")
public class CreditScore Report<E extends Exception> extends Report<E> {
private long id;
private ReportType type;
private String description;
...
...
}
I am looking to turn it into:
#MappedSuperclass
#Table(name = "report")
public abstract class Report<E extends Exception> {
#Id #Column(name="id")
private long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "report_type_id")
private ReportType type;
#column(name="description")
private String description;
...
...
}
#Entity
#Table(name = "report")
public class CreditScoreReport<E extends Exception> extends Report<E> {
public void doCreditScoreStuff(){
...
}
}
#Entity
#Table(name = "report")
public class DmvReport<E extends Exception> extends Report<E> {
public void doDmvStuff(){
...
}
}
I think you should use #Inheritance instead of #MappedSuperClass. Your code would look like this:
#Entity
#Table(name = "report")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "report_type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Report<E extends Exception> {
#Id #Column(name="id")
private long id;
#column(name="description")
private String description;
...
...
}
#Entity(name = "CreditScoreReport")
#DiscriminatorValue("1") // the id corresponding to the credit score report
public class CreditScoreReport<E extends Exception> extends Report<E> {
#Column(name = "specific_credit_score_report_1)
private Integer specificCreditScoreReport1;
public void doCreditScoreStuff(){
...
}
}
#Entity(name = "DmvReport")
#DiscriminatorValue("2") // the id corresponding to the DMV report
public class DmvReport<E extends Exception> extends Report<E> {
#Column(name = "specific_dmv_score_report_1)
private Integer specificDmvScoreReport1;
public void doDmvStuff(){
...
}
}
This strategy allows you to store credit score report and DMV report data in one table (report), but instanciate the proper entity according to the report_value_id field. You don't have to define the report_value_id in your parameters because it was already used to create the required entity.
Is this what you're looking for?
I have 2 Object A and B, which have same attributes, but different table.
#Entity
#Table(name = "A")
public class A {
#Id
private Integer id;
...
}
and
#Entity
#Table(name = "B")
public class B {
#Id
private Integer id;
...
}
it will cause duplicate code because every attributes are same.
What is best practice to do this kind of problem?
I'd create a #MappedSupperclass for this, and extend from it. It would look like this
#MappedSuperclass
public class Common {
#Id
private Integer id;
}
#Entity
#Table(name = "A")
public class A extends Common {
...
}
#Entity
#Table(name = "B")
public class B extends Common {
...
}
This doesn't create an entity hierarchy, but only moves common entity attributes in a super class. Often used for id, version, createdBy etc.
I have a problem with a QueryDSL query. Classes:
#Entity
#Table(name="project")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Project extends DomainObject implements Comparable<Project>, IconizedComponent, Commentable {
#ManyToMany(targetEntity=Student.class)
#JoinTable(name="project_student")
#Sort(type=SortType.NATURAL) //Required by hibernate
#QueryInit({"user"})
private SortedSet<Student> projectParticipants = new TreeSet<Student>();
private Project(){}
//attributes, get+set methods etc
}
#Entity
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class Student extends Role {
public Student(){}
//attributes, get+set methods etc
}
#Entity
#DiscriminatorColumn(name = "rolename", discriminatorType = DiscriminatorType.STRING, length = 8)
#Table(name="role", uniqueConstraints={#UniqueConstraint(columnNames={"user_id","rolename"}, name = "role_is_unique")})
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Role extends LazyDeletableDomainObject implements Comparable<Role> {
#ManyToOne(optional=false)
protected User user;
public Role(){}
//attributes, get+set methods etc
}
#Entity
#Table(name="user")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class User extends LazyDeletableDomainObject implements Comparable<User>, IconizedComponent {
private String firstName;
private String lastName;
public User(){}
//attributes, get+set methods etc
}
Query:
private BooleanExpression authorsNameContains(String searchTerm){
QUser user = new QUser("user");
user.firstName.containsIgnoreCase(searchTerm).or(user.lastName.contains(searchTerm));
QStudent student = new QStudent("student");
student.user.eq(user);
return QProject.project.projectParticipants.contains(student);
//java.lang.IllegalArgumentException: Undeclared path 'student'. Add this path as a source to the query to be able to reference it.
}
I have also tried annotating the projectParticipants set in Project with
#QueryInit("*.*")
But that gives the same exception. Any hints?
#Timo Westkämper
#siebZ0r
Thanks for your attention. Sorry for the delayed reply and incorrectly phrased question. Actually what I wanted to do was to write a working BooleanExpression.
In combination with the annotations already made, this was what I was after:
private BooleanExpression authorsFirstNameContains(String searchTerm){
return QProject.project.projectParticipants.any().user.firstName.containsIgnoreCase(searchTerm);
}
I got this right with the help of a colleague.