JPA how to annotate generic entity field conditionally? - java

I have this generic entity:
#MappedSuperclass
abstract class Position<T> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Enumerated(EnumType.STRING)
private T name;
}
But there's a case where the generic type is a String:
#Entity
class ChildPosition0 extends Position<String> {
}
And, JPA will complaint that String is not an enum in this case, but I need to annotate this name field if it's an enum, if not, the database will mark it as int type, and that's not ideal. How do I solve this? How to annotate the field conditionally?
My workaround:
Use Position as a parent class, and adding those field in child class individually, even though they share the same field:
#MappedSuperclass
abstract class Position {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
And extends it from child entity like this:
Child1:
#Entity
public class ChildPosition1 extends Position {
#Enumerated(EnumType.STRING)
private Priority name; // <- Priority is enum type
}
Child2:
#Entity
public class ChildPosition2 extends Position {
private String name;
}
This is too ugly IMO. And Java does not allow class field override from child class. So, back to the question: how to annotate generic field conditionally?

Related

How to query jpa entity in JOINED inheritance, without creating table for same?

I am trying to implemet inheritance hierarchy as mentioned in below image using hibernate Joined strategy.
Since Joined strategy creates table for entity regardless of entity class is Concrete or Abstract.
I don't want to create separate table for "CompanyEmployee" so I declared it as mapped superclass, but I should able to query this class/subclasses in polymorphic way.
Since it is mapped superclass I can't do this, and if I declare it to be entity it will create table which I want to avoid.
So, is there any way I can achieve this? I am thinking about mixed inheritance but from below quote it doesn't seems to be a good solution.
Mixed Inheritance
We should begin this section by saying that the practice of mixing inheritance types within a single
inheritance hierarchy is currently outside the specification. We are including it because it is both useful
and interesting, but we are offering a warning that it might not be portable to rely on such behavior,
even if your vendor supports it.
Inheritance hierarchy
#Entity
#Table(name="j_employee")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="emp_type", discriminatorType=DiscriminatorType.STRING)
public abstract class JEmployee extends AuditLog implements Serializable {
#Id
#Basic
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="employee_id")
private Integer employeeId;
#Basic
#Temporal(TemporalType.DATE)
#Column(name="join_date")
private Date joinDate;
#OneToOne
#JoinColumn(name="person_id", nullable=false)
private Person person;
#Column(name="emp_type", updatable=false, insertable=false)
private String empType;
//Getters and Setters
}
#Entity
#Table(name="j_contract_employee")
#DiscriminatorValue(value="JContractEmployee")
#PrimaryKeyJoinColumn(name="contract_employee_id", referencedColumnName="employee_id")
public class JContractEmployee extends JEmployee implements Serializable {
#Basic
#Column(name="daily_rate")
private Integer dailyRate;
#Basic
#Column(name="term")
private Integer term;
//Getters and Setters
}
//Don't want to create table for this class, but I should able to query this clas/subclasses in polymorphic way
#MappedSuperclass
public abstract class JCompanyEmployee extends JEmployee implements Serializable {
#Basic
#Column(name="vacation")
private Integer vacation;
//Getters and Setters
public Integer getVacation() {
return vacation;
}
public void setVacation(Integer vacation) {
this.vacation = vacation;
}
}
#Entity
#Table(name="j_part_time_employee")
#Access(AccessType.FIELD)
#DiscriminatorValue(value="JPartTimeEmployee")
#PrimaryKeyJoinColumn(name="part_time_employee_id", referencedColumnName="employee_id")
public class JPartTimeEmployee extends JCompanyEmployee implements Serializable {
#Basic
#Column(name="hourly_rate")
private Integer hourlyRate;
//Getters and Setters
}
#Entity
#Table(name="j_full_time_employee")
#Access(AccessType.FIELD)
#DiscriminatorValue(value="JFullTimeEmployee")
public class JFullTimeEmployee extends JCompanyEmployee implements Serializable {
#Basic
#Column(name="salary")
private Integer salary;
#Basic
#Column(name="penion")
private Integer pension;
//Getters and Setter
}

~"IdClass not defined" in JpaRepository, for an inherited #OneToOne #Id

I'm trying to create a jpa repository but there is a problem with a foreign-key primary-key. Although it is specified in the abstract base class (MessageDestination), it seems to be invisible from the repository of specialized MessageDestination class (e.g. MessageDestinationRoom).
[...] nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageDestinationRoomDAO': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: This class [class com.chat.message.entity.MessageDestinationRoom] does not define an IdClass
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Message implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#OneToOne(targetEntity = MessageDestination.class,
cascade=CascadeType.ALL, mappedBy="msg")
#NotNull
private MessageDestination dest;
//...
}
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class MessageDestination implements Serializable {
#Id #OneToOne(cascade=CascadeType.ALL)
private Message msg;
}
#Entity
public class MessageDestinationRoom extends MessageDestination {
#OneToOne #NotNull
private Room destRoom;
//...
}
public interface MessageDestinationRoomDAO
extends JpaRepository<MessageDestinationRoom, Message> {
public Set<MessageDestinationRoom> findMessageDestinationRoomByDestRoom
(Room dest);
}
To solve the issue I saw that I can annotate MessageDestination as a #MappedSuperclass, but this can't work because it needs to be an #Entity to be stored in Message. Sadly, it's not possible:
org.hibernate.AnnotationException: An entity cannot be annotated with both #Entity and #MappedSuperclass
Any ideas? Thanks...
Since you are using table per class inheritance strategy and you dont have any mapped superclass (so each entity must have its own id).
You can annonate your MessageDestination Entity as #MappedSuperClass and remove the #Entity from MessageDestination. As by default its each subclass will inherited all its field including the #Id field
Pending for a better answer because the only solution I found is quite ugly. That consists of splitting the primary and the foreign key, so there is redundancy...
This:
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class MessageDestination implements Serializable {
#Id #OneToOne(cascade=CascadeType.ALL)
private Message msg;
}
public interface MessageDestinationRoomDAO
extends JpaRepository<MessageDestinationRoom, Message> {
public Set<MessageDestinationRoom> findMessageDestinationRoomByDestRoom
(Room dest);
}
becomes this:
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class MessageDestination implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#OneToOne(cascade=CascadeType.ALL)
private Message msg;
}
interface MessageDestinationRoomDAO
extends JpaRepository<MessageDestinationRoom, Long> {
public Set<MessageDestinationRoom> findMessageDestinationRoomByDestRoom
(Room dest);
}
I was also getting same issue when I was using #oneToMany And #ManyToOne Annotation based Mapping.
Basically what I was doing mistake was in the class that was throwing the error "does not define an IdClass" was having composite Keys i.e More that one #Id annotation used over two member variables due to which it was getting considered as Composite Key and since hibernate expects a seperate Key class needs to be defined in case of composite key this failure was coming.

Jpa 2.0 - EntityManager.find (SomeEntity.class,PK) need to fill Descriminator value to key

I have a problem, I have two entity Job and JobPK
Job class looks like this sample code :
#Entity
#IdClass(JobPK.class)
#Table(name="JOB")
#Inheritance
#DiscriminatorColumn(name="JOB_TYPE")
public abstract class Job implements Serializable {
#Id
#Column(name="FOLDER_ID")
private BigDecimal folderId;
#Id
#ColumnDefinition(position = 1)
private String name;
#Column(name="JOB_TYPE",insertable=false,updatable=false)
private String jobType;
...
}
and JobPk :
public class JobPK implements Serializable {
private static final long serialVersionUID = -3266336718203527905L;
#Column(name="JOB_TYPE",insertable=false,updatable=false)
private String jobType;
#Id
private String name;
#Id
#Column(name="FOLDER_ID")
private BigDecimal folderId;
......
}
I have two class which extends Job : CalculatingJob and ImportingJob
Now I wont to use :
getEntityManager().find(CalculatingJob.class, new JobPK (BigDecimal.valueOf(folderId),name))
and I have problem because I must fill i JobPK descriminator value field. If I don't do that I've got Null Pointer Exception. Descriminator value is in key by default I think but I don't want put information about descriminator value explicite during JobPk creating. I thought that Entity which extends from Job will fill this field automaticaly. Any Idea to bypass this problem, maybe I can get Annotation #DescriminatorVale from CalculatingJob and then put into constructor JobPk
Thanks for Help
Try this configuration for Hierarchy structure
Job.java
#Table(name = "JOB")
#Inheritance
#IdClass(JobPK.class)
#DiscriminatorColumn(name = "JOB_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class Job implements java.io.Serializable {
}
CalculatingJob.java
#Entity
#DiscriminatorValue("CalculatingJob")
public class CalculatingJob extends Job {
}
ImportingJob.java
#Entity
#DiscriminatorValue("ImportingJob")
public class ImportingJob extends Job {
}
JobPK.java
public class JobPK implements Serializable {
}
The discriminator value is entered by hibernate.

Hibernate map subclass ignoring parent

I'd like to map lots of subclasses with a common parent class : B extends A, C extends A,... but the database doesn't care A. And B and C have nothing in common (no Id, no reference...).
public class A {
#Id
#Column(name="id")
private Long id;
#Column(name="reference", nullable=false)
private String reference;
}
Is it possible to do this without adding #Entity ?
SOLUTION
#MappedSuperclass
public class A {
#Id
#Column(name="id")
private Long id;
#Column(name="reference", nullable=false)
private String reference;
}
#Entity
#Table(name="B")
public class B extends A {
}
Use #MappedSuperclass on class A:
Designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it.

jpa: inheritance with self references and non abstract superclass

In my current project I have a inheritance structure that looks like this:
#Entity
#Table(name="groups")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorValue("G")
#DiscriminatorColumn(name="group_type")
public class Group{ //some annotations removed
private Long id;
private String name;
private Set<Subject> subjects;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="parent_group_id")
private Group parent; ##### tree parent ####
#OneToMany(cascade=CascadeType.ALL, mappedBy="parent")
private Set<Group> subGroups; ##### tree children #####
...
}
My Group objects can have kind of a tree like structure by containing a list of other Group objects.
As some groups are a bit special, there is a second class that extends this class:
#Entity
#DiscriminatorValue("C")
#Table(name="fix_groups")
public class FixGroup extends Group{
private Layout lay;
private Set<Person> instr;
...
}
I tried to use a joined multi table approach (as described here: http://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Joined.2C_Multiple_Table_Inheritance) but it seems not to work with a non abstract superclass like Group!
I get the following exception:
Caused by: java.lang.ClassCastException: org.hibernate.mapping.JoinedSubclass
cannot be cast to org.hibernate.mapping.RootClass
Is there a solution apart from declaring Group as abstract and making a new class Group2 that only extends it?
And if I did so, would this self-reference Set<Group> subGroups still cause problems?
I was able to cause this error by setting the ID in the subclass when it is already mapped in the parent class (in this case Group). For example in the parent class:
#Entity
#Table(name="groups")
#Inheritance(strategy=InheritanceType.JOINED)
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
...
and then setting the id in the subclass like so:
#Entity
#Table(name="sub_groups")
public class SubGroup extends Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
...
In this case, Group does not need to be abstract, but you can't define the id on the subclass.
Also, as a side note, if you are using Hibernate with an inheritance of type "join", then the discriminator column and value are not needed. Hibernate only utilizes those if using a single table. Please reference this post for further information:
Hibernate 4: persisting InheritanceType.JOINED discriminator column values

Categories