Hibernate ManyToMany and superclass mapping problem - java

I need to create a relation in Hibernate, linking three tables: Survey, User and Group.
The Survey can be visible to a User or to a Group, and a Group is form of several Users.
My idea was to create a superclass for User and Group, and create a ManyToMany relationship between that superclass and Survey.
My problem is that Group, is not map to a table, but to a view, so I can't split the fields of Group among several tables -which would happen if I created a common superclass-.
I thought about creating a common interface, but mapping to them is not allowed.
I will probably end up going for a two relations solution (Survey-User and Survey-Group), but I don't like too much that approach.
I thought as well about creating a table that would look like:
Survey Id | ElementId | Type
ElementId would be the Group or UserId, and the type... the type of it.
Does anyone know how to achieve it using hibernate annotations? Any other ideas?
Thanks a lot

I posted a very similar answer yesterday. To summarize, you can't use a mapped superclass because a mapped superclass is not an entity and can't be part of an association (which is what you want) but you can use an abstract Entity with a TABLE_PER_CLASS inheritance strategy to obtain a similar result.
Something like this (not tested):
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEntity {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#ManyToMany(mappedBy="entities")
private Set<Survey> surveys = new HashSet<Survey>();
...
}
#Entity
public class User extends AbstractEntity {
...
}
#Entity
public class Group extends AbstractEntity {
...
}
#Entity
public class Survey {
#Id #GeneratedValue
private Long id;
#ManyToMany
private Set<AbstractEntity> entities = new HashSet<AbstractEntity>();
...
}
References
Annotations, inheritance and interfaces
using MappedSuperclass in relation one to many
Polymorphic association to a MappedSuperclass throws exception

You can use the table per concrete class inheritance strategy, hibernate will replicate all properties for each subclass, this will work with a view.
I would also suggest the composite pattern for users/groups (which is close to your first option).
http://en.wikipedia.org/wiki/Composite_pattern

This is possible. Such an 'inherited properties' approach can be achieved by defining the superclass as a MappedSuperclass.
EDIT:
There is also some alternatives listed in section 2.2.4 in the hibernate annotations reference doc, section 2.2.4.4 covers MappedSuperclass.

Related

Spring repository for super entity class not fetching all subclass entity types when changed from #MappedSuperClass to #Inheritance

We have an abstract #MappedSuperClass and bunch of entities extending it, like:
#MappedSuperclass
public abstract class SuperEntity implements Serializable {
#Id
private Long id;
private String name;
}
and lots of entities like:
#Entity
public class Sub[1..20]Entity extends SuperEntity {
...
}
Because of this there were created - as well - a bunch of repositories for each entity. All well this far.
Now there is a need to fetch all the entities that extend super. Therefore SuperEntity was changed as below:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class SuperEntity implements Serializable {
This should be functionally almost the same (is it?).
Then a new repository for this was created, like:
public interface SuperEntityRepository extends JpaRepository<SuperEntity, Long> { };
Now the problem is, when calling:
superEntityRepository.findAll();
it returns only about 5 of sub entities not all the 20. What could be wrong?
Upon writing the question I realized what was the problem. Values - including IDs - were inserted straight to the database and IDs were not unique in the scope of SuperEntity. That is why there is no #GeneratedValue, BTW. IDs were only unique in the scope of each extending sub class entity.
There were no error messages. Spring repository just picked up the first found id and all the other entities with same ID were ignored.
So the answer to have this working is to to update all the extending entities to have unique ID in the scope of the SuperEntity.
However, updating references cascading is quite a job so if there is a lighter way to get this working, share it.
Yes, I could have deleted the question but maybe someone finds it and this answer useful

Classes Relationships with JPA

I have a set of Java classes with the following UML diagram:
public class Invoice {
#Id
private long id;
...
}
public class InvoiceDetail {
#Id
private long id;
...
private String productName;
private int quantity;
private double price;
}
My purpose is using JPA annotations to establish the different relationships between them. There is a composition relationship between Invoice and InvoiceDetail, which is resolved using #Embedded and #Embeddable annotations for Invoice and InvoiceDetail respectively. However, a problem appears by establishing the relationships between InvoiceDetail, Class3 and Class4. In these relationships InvoiceDetail must be annotated as #Entity. However, when a class is annotated at the same time as #Entity and #Embeddable, the corresponding server will throw a runtime error during the deployment.
Basing on the information of this website, I have written the following possible solution:
#Entity
public class Invoice {
#Id
private long id;
...
#ElementCollection
#CollectionTable(name="INVOICEDETAIL", joinColumns=#JoinColumn(name="INVOICE_ID"))
private List<InvoiceDetail> invoiceDetails;
...
}
Would be this right in order to resolve my problem?
Thanks in advance.
Although without knowing what the classes really are it is hard to tell, I suppose that you have a design problem. The composition between Class1 and Class2 says that any Class2 instance only exists within the lifecycle of a corresponding Class1 instance. But on the other hand you have Class3 instances and Class4 instances which can / must have a relationship to a Class2 instance.
What I'm trying to say is that from my point of view the relationship between Class1 and Class2 should be a simple association and not a composition. Following this path Class2 would be an Entity in JPA and then you should have your problem solved.
I usually use #Embeddable for classes whose instances never exist by themselfes and #Entity for any class whose instances can exist without other instances. An address for example could be implemented either way but not on the same system. Address would be #Embeddable if I don't want to link addresses but it had to be #Entity if I want to make sure the same address isn't saved in more than one row.
[edit: added after classes 1 and 2 were renamed to Invoice and InvoiceDetails]
Having a composition between Invoice and InvoiceDetails makes perfect sense. But I still think you should avoid the need of double personality for InvoiceDetails. I can think of two solutions (both refactorings):
If you prefer having InvoiceDetails as #Embeddable you could change the associations of Class3 and Class4 to Invoice instead of InvoiceDetails. InvoiceDetails would still be traversable via the Invoice object.
If you prefer keeping the associations as is you could declare InvoiceDetails to be an entity. You could still achieve your composition with a cascading delete (see javax.persistence.CascadeType). As it seems that InvoiceDetails already has it's own table, this probably is the better option.
I checked my JPA applications and haven't found any occurence of the same class being #Entity and #Embeddable. Honestly, I doubt if this is possible at all because the official javadoc of #Embeddable says:
Specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity.
As #Entity has it's own identity, you would try to declare the same object having two identities - and this can't work.
[/edit]
[edit2: adding code for solution proposal #2]
This code should work with some assumptions (see below). This is the implementation of bi-directional navigation for a 1:n-relationship.
#Entity
public class Invoice {
#Id
private long id;
#OneToMany(mappedBy="invoice", cascade = CascadeType.ALL)
private List<InvoiceDetail> details;
}
#Entity
public class InvoiceDetails {
#Id
private long id;
#ManyToOne
#JoinColumn(name="invoice_id")
private Invoice invoice;
}
Assumptions: Tables are named like the entities, the foreign key column for invoice_details table is named "invoice_id" and both tables have a primary key column named "id". Note that the mappedBy-value "invoice" refers to the entity field while the name-value "invoice_id" refers to the database table.
Be cautious when deleting an Invoice object whose InvoiceDetails still are referenced by your Class3 or Class4 instances - you have to release these references first.
For information about JPA refer to these resources:
The Java EE 7 Tutorial: Persistence
Wikibooks: Java Persistence
Javadoc of Package javax.persistence
[/edit]

Hibernate associations with an interface

I'm hoping someone can help me figure out a problem I am having with hibernate.
I am working on a billing system where different types of entities can be invoiced. The entities implement an "Invoicable" interface.
In my invoice class I have this line
#ManyToOne(fetch = FetchType.EAGER)
private Invoiceable responsibleEntity;
The invoiceable interface looks like this
#MappedSuperclass
public interface Invoiceable
{
// Name to display as entity responsible
String getInvoiceOwnerName();
// Get email address to send invoice
String getInvoiceOwnerEmail();
}
I'm getting an exception when I run this
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on models.Invoice.responsibleEntity references an unknown entity: interfaces.Invoiceable
I've tried googling it and reading some similar posts but I can't get anything to work. Does hibernate support this type of mapping?
Thanks
You have to use Inheritance in the Hibernate way to do this. Details can be found in the --> Hibernate Inheritance Doc
Additional information:
Hibernate is not able to wire types together where no database table is behind it. What does this mean for you. MappedSuperclasses do not have a table so there's nothing to wire together. So this is out of business. Most important keep in mind. When you request to read all parent objects. To which tables should he join? This is the reason why the inheritance system of hibernate comes in to play. You have to change the interface to a normal class. This should not be to much of a problem. Now choose an inheritance Strategy of your choice. Each of them have their pros and cons. Please read the doc to get an idea of which is best for your situation.
The following is an example of a the single table approach with an Invoice class.
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="invoiceType",
discriminatorType=DiscriminatorType.STRING
)
#DiscriminatorValue("Invoice")
public class Invoiceable { ... }
#Entity
#DiscriminatorValue("CustomInvoice")
public class CustomInvoice extends Invoiceable { ... }

JPA/Hibernate inheritance without id

Is it possible to make inheritance in JPA/Hibernate without id?
My use case:
I have multiple entities and I want every time change is being done, timestamp being recorded. So I created AbstractResource and want each derived class inherit properties and logic (to avoid writing same stuff over and over again in each class).
My problem that hibernate wants an ID to entity, and I do not care about id, since my only concern is additional properties. And each entity can have whatever id it wants (String, int, long, different name, etc.).
I tried with Embeddable, but looks like hibernate does not support inheritance for Embeddable. Do you have any ideas, how my task can be achieved?
My parent class from which "Audited" entities are derived:
#Embeddable
#EntityListeners(AbstractResourceListener.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class AbstractResource {
private long modifyTimestamp;
#Column(name = "_MODIFY_TIMESTAMP", nullable = true)
public long getModifyTimestamp() {
return modifyTimestamp;
}
public void setModifyTimestamp(long modifyTimestamp) {
this.modifyTimestamp = modifyTimestamp;
}
}
#MappedSuperclass is an annotation for super classes that you can extend and use in audit. Please see example

Correct database design for JPA entity inheritance

I'm starting a new project using JPA 2 + Hibernate 4.2.6 for data access.
I have two tables in my DB representing two different kinds of Answer, so I have answer_type_a and answer_type_b tables.
The are identical except for one field.
Now I'm creating my model classes and I'd like to inherit my AnswerA and AnswerB entities from a commom super-class or interface Answer.
I read some docs about entity inheritance:
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/inheritance.html#inheritance-tablepersubclass
but I have not a clear view on how to structure my DB and my entity classes to achieve this. Can you help me?
You need table Answer - that is super for particular others, where you should place all common columns for answer_type_a and answer_type_b. Assume Answer.a_id is primary key, then answer_type_a.a_id answer_type_b.a_id are simultaneously PRIMARY KEY and FOREIGN KEY.
Of course don't forget to place distinguished columns to answer_type_a and answer_type_b
+---------+
| Answer |
+---------+ ----------------------->--+--------------+
| a_id +------) | answer_type_b|
---->-+--------------+ +--------------+
... | answer_type_a| | a_id |
+--------------+ ...
| a_id |
....
You need to create one class named Answer with the common fields and you will annotate it with #MappedSuperclass. Additionally, you will have 2 entity classes, named AnswerA and AnswerB which will only hold the "extra" fields.
If you have 2 tables, then you need to use the TABLE_PER_CLASS inheritance type. So add #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) to the Answer class, and probably a #Table annotation to AnswerA and AnswerB.
But if I were you, I would use one table for all answers, with all the needed columns + one discriminator column so you can distinguish if a row is answer type a or type b.
Well, as I understand your question is about mapping:
You need a java class Answer which is actually your super class. As there is no corresponding DB table for that, it must be annotated with #MappedSuperclass
#MappedSuperclass
public class Answer {
//fields&properties
}
And now the two classes come, which are entities, as they are persisted in DB tables:
#Entity
#Table("answer_type_a")
public class AnswerA extends Answer {
//here you add the field that is not common with AnswerB
}
#Entity
#Table("answer_type_b")
public class AnswerB extends Answer {
//here you add the field that is not common with AnswerA
}
PS: IMHO you don't need any inheritance annotations, as the mapped super class is not an entity.
I recently was in a similar situation like you. The best solution found was using an abstract class as a super type, and extending that class to the concrete types (which will represent your entity).
So for your situation you would model something like this.
#MappedSuperClass
public abstract class Answer{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer id;
#Column
String commonFieldOne;
#Column
String commonFieldTwo;
#Column
String commonFieldThree;
...
}
Note that I used the #MappedSuperClass annotation. This tells JPA that this is a super class that will not be instantiated and should not have a database table associated with it.
Also note that the Id which will serve as the primary key for both child tables is defined in this class
Now for you concrete classes (which will be mapped to tables in you database), you would use the #Entity annotation.
#Entity
#Table("answer_type_a")
public class AnswerA extends Answer{
//All common fields from Answer table will be included in the DB tables as columns automatically
#Column
String uniqueFieldOne;
....
}
You can do the same for #Table("answer_type_b")
Hope this helps!
Just create one table in your database e.g answer which contains both fields. You must have three classes Class 1 : Answer, Class 2: Answer_Type_A extends Answer, Class 3: Answer_Type_B extends Answer. There will be an hbm file for Answer (superclass) and two hbm for each Answer_Type_A and Answer_Type_B. In sybclass hbm files include:
<hibernate-mapping> <subclass name="com.test.Answer_Type_A"
extends="com.test.Answer">
<property name="fieldA" />
</subclass>
</hibernate-mapping>

Categories