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)
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.
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.
Lets say that class 'X' is mapped to table 'X' class 'A' is mapped to Table 'A' and Class 'B is mapped to table 'B'.
Table X Structure:(X_ID, some other columns
Table A Structure:(A_Id,X_Id, some other columns)
Table B Structure:(A_Id, some other columns)...Table B also has A_Id
Class 'B' extends class 'A'. We have the mapping files for both of them as:
Class 'A' Parent Mapping file:
#Entity
#Table(name = 'A')
#Inheritance(stratergy=InheritanceType.Joined)
public abstract class A {
#Id #Clumns(name = "A_Id)
#GeneratedValue
protected Long aId;
-- some more A specific fields
}
Class 'B' Mapping file:
#Entity
#Table(name= 'B')
Public class B extends A{
---- B specific fields
}
Now, I have a SQL Query as below that I need to write using hibernate criteria API.
select * from X
INNER JOIN A
ON X.id = A.id
INNER JOIN B
ON A.id = B.id
where B.name = 'XYZ'
and B.Sex = 'M'
I have come up with:
Criteria c = session.createCriteria(x.class, "x");
.createAlias("x.a", "a")
.createAlias("a.b", "b")
.add(Restrictions.eq("b.sex", "M"))
.add(Restrictions.eq("b.name", "XYZ"));
But, if we check the mapping file, there is no direct reference of B in A. Hence hibernate throws out "B not related to A" entity.
Is there any way this inheritance can be mapped in query crteria
You shouldn't need to reference A at all in your criteria, or use any aliases.
Criteria c = session.createCriteria(B.class);
.add(Restrictions.eq("sex", "M"))
.add(Restrictions.eq("name", "XYZ"));
will give you the result you need.
Because of the InheritanceType.Joined, this will probably produce SQL that includes a join to the the A table (something close to the sql you show), but it isn't necessary to specify that join in the criteria.
The things that look like columns in the criteria are actually (reflective) references to fields in your Java objects. Hibernate figures out the columns to put in the sql from your annotations, and should the join to the A table if it's needed based on the inheritance annotation.
To be sure of this in your context, and to understand all this a bit better, I'd advise trying it and turning on logging of the generated sql as described in this answer to another SO hibernate question.
Try this way:
Criteria rootCrit = session.createCriteria(A.class);
rootCrit.createAlias("B", "B");
rootCrit.add(Restrictions.eq("B.sex", "M"));
rootCrit.add(Restrictions.eq("B.name", "XYZ"));
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)