First of all I've been all the morning seeking for a solution for my problem. I've found similar problems but no one fixed my problem :(
I have a 3 level inheritance on my Java classes model:
A <-- B <-- C
Mapping this into a Relational Data Base I've made
A (1) -- (0..1) B (1) -- (0..1) C, so my annotated Java classes are something like:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="type", discriminatorType=DiscriminatorType.INTEGER)
#XmlRootElement
public class A implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long idA;
private Integer type;
...
}
#Entity
#DiscriminatorValue("2")
#PrimaryKeyJoinColumn(name="idB", referencedColumnName="idA")
#XmlRootElement
public class B extends class A implements Serializable{
private Long idB;
private Integer type;
#OneToOne(cascade={CascadeType.REMOVE}, fetch=FetchType.LAZY)
#JoinColumn(name = "idB")
private A a;
...
}
#Entity
#DiscriminatorValue("8")
#PrimaryKeyJoinColumn(name="idC", referencedColumnName="idB")
#XmlRootElement
public class C extends class B implements Serializable{
private Long idC;
#OneToOne(cascade={CascadeType.REMOVE}, fetch=FetchType.LAZY)
#JoinColumn(name = "idC")
private B b;
...
}
I don't know why, invoking a Query SELECT c FROM C c retrieves me 0 result when I have info on that table. If I do the query SELECT b FROM B b it works fine. It seems to be something wrong with the hierarchy annotations I've used.
Any idea? Thank you in advance :)
Joined table inheritance means that when you have a C entity, there must be a row in the A, B AND C tables. If you have data in C but no corresponding data in A or B that match up, then you will get no C entities back - the data is invisible to JPA because it first queries the A table to see if there are rows with a Type=3, and then joins it to B and C.
So check that your java inheritance model actually matches the data model. You also show that B and C have a reference to their parent - this cause the same id field to be mapped twice, as it is in both the Entity's PrimaryKeyJoinColumn and the mapping's JoinColumn. Since the referenced A (or B) is apart of the B (or C), there is no need to also have an object referenence to it - any of the inherited get methods will return the data from the A table anyway.
Related
Have a relation between a sub table and a related table using the primary table. Any thoughts about how to go about the hibernate relation.
A <---> B (One to One relation) B is a sub-table of A.
B <---> C (B has One to Many Relation with C Using A)
It not needed in table B in your case. There is #OneToMany between A and B.
#Entity
#Table(name="A")
public class A {
....
#OneToMany(mappedBy="a")
private B b;
}
#Entity
#Table(name="C")
public class C {
...
#OneToOne
#JoinColume(name="ref_to_a")
private A a;
}
Additional table B is required if #ManyToMany association take place.
I am sorry if the Subject was misleading but here is the scenario that I have.
#Entity
Class A {
#Id
private String id;
#OneToMany
private Set<B> b;
}
#Entity
Class B {
#Id
private String c;
private String d;
}
#Entity
Class C {
#Id
private String e;
private String f;
}
Currently, I am retrieving the entity A with its set of objects from Entity B. The thing is that Entity B and Entity C are not related by foreign key and they need to be joined by their primary keys, so when I retrieve the entity A, in the set of objects, I will get the columns from the entities B and C joined together.
Is there any way to tell Hibernate to join the columns from entities B and C when I try to get the object A ?
In JPA 2.0 for joining related entities join is used. For this FK was needed.
Problem with joining unrelated entities is solved in JPA 2.1 standard, Hibernate 5.1 and above. You can use join on unrelated columns.
i have a question that i'm not sure its possible with jpa
if i have let say table A
public class A {
Long id;
Long type;
Details details; // can this connect to B or c depends on type?
}
and table B and C are two different details tables. (not sure anything in common)
can i connect B or C to A depends on A.type ?
thanks
Alon
EDIT: let me try to be more accurate
i have entity
#Entity
#Table(name = "tbl_A")
public class A implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
private Long id; // seems like spring's jpa has issue hanlde "_"
/*
*can i achive some thinglike
* if detailsTableType== 1 use Details as B table
*/
#OneToOne(mappedBy = "details", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private B details;
/*
*can i achive some thinglike
* if detailsTableType== 2 use Details as C table
*/
#OneToOne(mappedBy = "details", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private C details; //Details
/*
* this field set the from where the join should be
*/
private Long detailsTableType;
}
note the B,C not necessarily share anything in common
EDIT 2:
one possible solution can be some kind of hack using getter. which means:
map in A all the possible join(mark them as lazy) and create a getter that will know depend by type which join it should use..
I guess that you want to add some details to A instance with inheriting B and C class from A class.
I recommand you to read documentation about inheritance and discriminator value/column.
On the documentation of hibernate you will see how to
- use the concept of inheritance and TABLE_PER_CLASS strategy
- and describe the descriminator column (your type column)
Edit : but i recommand you to use another strategy like SINGLE_TABLE_PER_CLASS. Note that a JPA provider don't have to support the TABLE_PER_CLASS strategy, and also, this particular startegy have consequences on performance.
*Second Edit : * Ok : i would suggest you to use polymorphism for B and C class because they use something in common => the link whith the base class A !
You could have :
Class A
* Member list of DetailsItf
and
DetailItf (interface)
|
AbstractDetail (SINGLE_TABLE_PER_CLASS strategy with discriminator colum ou TABLE_PER_CLASS) implements DetailItf
|
|-B inherits from Details
|-C inherits from Details
Then you have to use an AbstractDetail class in your base A class like :
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany( targetEntity=foo.pck.AbstractDetail.class )
private List<DetailsItf> details = new ArrayList<DetailItf>();
And on usage, you should make a
B myB = new B();
// saving myB entity
A myA = new A();
myA.addDetails(myB);
// saving myA
You should also do specific queriez basing you on TYPE() JPQL specific keywords or, on using FROM CLASS in jpql but you have to create a proof of concept and validate performances.
Suppose there are three classes:
One is:
#Entity
#Indexed
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
#Table(name = "tbl_a")
public class A {
#Id
#DocumentId
#GeneratedValue(generator="rwSpecSeq")
#SequenceGenerator(name="rwSpecSeq", sequenceName="RW_SPEC_SEQ")
private int id;
private String sampleAttribute;
//Getters and Setters
}
#Entity
#DiscriminatorValue("b")
public class B extends A {
}
#Entity
#DiscriminatorValue("c")
public class C extends A {
}
If one object is saved as B's object then if again I want to cast or change the object to C then it is giving class cast exception. Please tell me how to cast class in hibernate when merging.
My approach was something like this:
//Assuming that the object is C class' object.
A a = adminService.getA(request.getParameter("id"));
//before merging I did this
B b = (B)a;
adminService.save(b);
You just can't. Hibernate keeps the objects type when they are persisted to the database. And Java prevents casting objects from one type to another when they are incompatible.
In your case, you can't cast an instance of C in B. It would be like casting a String into an Integer.
Your best shot is to write a method that takes an instance of C and copies all the attributes of C into a new object of type B.
What does the adminService.getA(request.getParameter("id")); actually do? If it does something like:
hibernateSession.get(A.class,id);
then Hibernate will fetch the corresponing values from the row with that id, and create an instance of the class A you passed as argument (that's why you pass it there, from the javadocs: "Return the persistent instance of the given entity class with the given identifier"). This means that even if you've declared your Hibernate inheritance such that Entity C extends Entity B extends Entity A, and the corresponing classes extend eachothers in the same way, Hibernate will actually create an instance of A (not an instance of B or C which you adress as A), so that instance of A cannot be cast to B.
If however the id there corresponds to a row which is of type B (let's say), then if you do
A a = hibernateSession.get(B.class,id);
then you can do
B b = (B)a;
because your "a" objet is infact of type B (even though you initially treat it as A). However in this situation it would be simpler to just do:
B b = hibernateSession.get(B.class,id);
Again, the above will work if you actually saved an entity of type B (or C) in the row with that id (i.e. your discriminator colum contains the value for type B or C)
I have 2 test classes:
Class A{
B b;
// some other properties
}
Class B{
// some properties
}
every instance of A has only one instance of class B, but an instance of class B can be assigned to more than one instance of class A
like :
B b = new B();
A a1 = new A();
a1.setB(b);
A a2 = new A();
a2.setB(b);
what type of association is this?
At first I was thinking about a one-to-one unidirectional but maybe is a many-to-one? but I don't have any collection of A objects on B.
Can someone explain me what is the correct way to implements this association (using annotation)?
every instance of A has only one instance of class B, but an instance
of class B can be assigned to more than one instance of class A
So , A to B is the many-to-one relationship . B to A is the one-to-many relationship.
The following shows the bi-directional mapping between A and B using annotation:
#Entity
#Table(name="tableA")
Class A{
#ManyToOne
#JoinColumn(name = "B_ID")
B b;
// some other properties
}
#Entity
#Table(name="tableB")
Class B{
#OneToMany(mappedBy = "b")
List Set<A> listOfA= new ArrayList<A>();
// some other properties
}
Important points:
#Entity marks the java class as an hibernate entity. It is mapped to the name of the table specified in the #Table
If no #Table is specified ,by default , it is mapped to the table with the name that is equal to the unqualified class name of the entity.
#ManyToOne defines Class A to Class B 's relationship is many-to-one
In the relational database , one-to-many relationship is expressed by using the following foreign key constraint :
"Many side table" has a FK column which only accepts the PK of the "one side table".
This name of this FK column can be defined explictly by the name attribute of #JoinColumn. If #JoinColumn is not specified , then default value(s) with be used for this FK column , which concatenates with the name of "one side table", _ (underscore), and the name of the PK in the "one side table".
#OneToMany defines Class B to Class A 's relationship is one-to-many.
The relationship is:
B (One) ---to--> A (Many)