Get other side of association from persistent property - java

I have got two entities:
class A{
#Id
private int id;
#OneToMany(fetch = FetchType.LAZY , mappedBy = "a")
private List<B> bList;
}
class B{
#Id
private int id;
#ManyToOne(fetch = FetchType.LAZY, targetEntity=A.class)
#JoinColumn(name = "A_ID", referencedColumnName = "id", nullable = true)
private A a;
}
For example I have got "a" persistent property from B class and I need to retrieve the other side of association - "bList" persistentProperty of A class. But I don't know the name of the other side property. How could I do that? Association obverse property is null every time. And I don't want to use doWithProperties() method and check fields for equality by class, because there are might be some other properties with the same class.
I was trying to make something like this:
PersistentEntities persistentEntities;
Class aClass = A.class;
Class bClass = B.class;
PersistentProperty<?> obverse = persistentEntities.getPersistentEntity(bClass).getPersistentProperty("a").getAssociation().getObverse();
But it is null every time. Maybe I am missing something. Thanks for help!

Related

Hibernate JPA OneToOne Relationship with a foreign key

I'd like to create a OneToOne relation between two entities(also tables in mysql). When i get a post request i want to save the A entity and then B entity is saved automatically, because there is a column in B ("a_id") that is foreign key of the primary key in A ("id_a"). How can i map them correctyl?
#Table("a_tb")
public class A(){
#Column(name="id_a")
private Long idA;
#OneToOne(mappedBy = "aId", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, orphanRemoval = true, optional = false)
private B b;
}
#Table("b_tb")
public class B(){
#Column(name="id_b")
private Long idB;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private Long aId;
}
Have tried to set idA to A type, but when i call the get method he will loop calling A with B innested that as A innested and so on..
I just want to save the id of A in B, not the entire object, otherwise he will return the entire object when i call the get method and the loop is back.
The mapping is correct, not the autoset of the value in a_id in B.
What I expect is: when i create A, B gets automatically the id of A and save it to its column ("id_a") in db when i request to save object A. That's should work with delete too (when i delete A, B is deleted too).
The matched JSON will be:
{
"a":{
"id_a": "value",
},
"b":{
"id_b": "value",
"a_id": "id_a"
}
}
Thanks in advice.
from the first look it looks like
#OneToOne
#JoinColumn(name = "idA_id")
A a;
would be the change you need and
You need to provide one to one mapping [Reference] in both the classes.
If parent reference, not setup automatically you can create a transient variable and set parent's primary key using it.
Try this Example:
#Table("a_tb")
public class A(){
#Column(name="id")
private Long id;
#OneToOne(mappedBy = "aId", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, orphanRemoval = true, optional = false)
private B b;
}
#Table("b_tb")
public class B(){
#Column(name="id")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
#Transient
private Long aId;
// getters and setters
public void setAId(Long aId){
if(aId != null){
this.a = new A();
this.a.setId(aId);
}
}
}

JPA Entity field reference OneToOne recursive

I am getting this error when I will persist() my entity. I think that the cause of the error is the relation, my idea is that FolderEntity (represents a virtual folder) can be stay inside another (only one) Then I created the reference to self (In the extended class, because all resources can be inside a folder, and folder is an resource)
org.hibernate.AnnotationException: Referenced property not a (One|Many)ToOne: com.editor.entity.FolderEntity.id in mappedBy of com.editor.entity.FolderEntity.folderId
This my main Entity:
#MappedSuperclass
public abstract class Entity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", nullable = false)
private Integer id;
/** getter/setter **/
}
Then I extends it in my ResourceEntity Entity:
#MappedSuperclass
public class ResourceEntity extends Entity {
#Column(name = "NAME", length = Lengths.NAME40, unique = true, nullable = false)
private String name;
#Column(name = "DESCRIPTION", length = Lengths.DESCRIPTION1000, unique = false, nullable = true)
private String description;
#JoinColumn(name = "FOLDER_ID", updatable = true, nullable = false)
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "id")
private FolderEntity folderId;
/** getter/setter **/
}
Finally, I am working with this entity:
#javax.persistence.Entity
#Table(name = "EDITOR_FOLDERS")
#NamedQueries({
#NamedQuery(name = FolderEntity.ALL_FOLDERS, query = "select f from FolderEntity f"),
#NamedQuery(name = FolderEntity.FOLDER_BY_NAME, query = "select f from FolderEntity f where name = :name and resourceType = :resourceType") })
public class FolderEntity extends ResourceEntity {
public static final String ALL_FOLDERS = "findAllFolders";
public static final String FOLDER_BY_NAME = "findAllFoldersByName";
#Column(name = "RESOURCE_TYPE", length = Lengths.CODE, unique = false, nullable = false)
private Integer resourceType;
/** getter/setter **/
}
Anybodys help me to solve this? Thanks!
You should check the meaning of mappedBy: It does not reference the field that contains the ID (JPA is clever enough to find that one by itself), but it references another XToOne field that "owns" the mapping
public abstract String mappedBy
(Optional) The field that owns the relationship. This element is only specified on the inverse (non-owning) side of the association.
(from javadoc of OneToOne)
In your case you don't need the mappedBy as you are on the owning side. And you should name the attribute folder as you are referencing no ID but an entity.
Another remark: Use an enum for resourceType if you intend to define the possible values in your application as constants.

JPA Mapping - Unique combination of parents per child

I am using:
Spring 3.2
Hibernate 4.1.9
I need to map, with JPA, three classes. Class A has a ManyToMany relationship with Class B. A unique combination of Class A and Class B need to own a collection of Class C.
Table A
foo
id | name
Table B
bar
id | name
Table C
data
id | xrefId
Join Table -- Unique Key on (fooId,barId)
xref
id | fooId | barId
Altering the existing data structure is not an option.
Edit 1:
Goal: Load a Foo, get its collection of Bars. From each Bar, get its (their!) collection of Data.
Class A
#Entity
public class Foo {
#Id
private UUID id;
#ManyToMany(optional = false)
#JoinTable(name = "xref",
joinColumns = { #JoinColumn(name = "fooId") },
inverseJoinColumns = { #JoinColumn(name = "barId") })
private List<Bar> lstBar = new ArrayList<Bar>();
}
Class B
public class Bar {
#Id
private UUID id;
#ManyToMany(mappedBy = "lstBar")
private List<Foo> lstFoo = new ArrayList<Foo>();
}
Class C
public class Data {
#Id
private UUID id;
}
Just KISS. Make another class Xref, which contains id, foo, bar and Set<Data> fields. Make a DAO method to find an Xref using two parameters foo and bar (implement it with a simple HQL). The unique requirement could be achieved by an unique constraint in the database.
It doesn't look good trying to express it just by the class hierarchy, better to use DAOs.
Your join table, xref, has an extra id field, in order to be able to create such a table with JPA you need an extra entity class XRef and then you have to map the relation between A and XRef and betweem B and XRef (both are one-to-many). Then, you can create the entity class C and map the relation between C and XRef. Do you need more help? I don't have time right now to provide the code, but if you need ask and I will try to add it as soon as possible.
Look at this example (used Integer instead of UUID for simplicity, the rest should be OK).
Bar class:
public class Bar {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "barId")
private Collection<Xref> xrefCollection;
}
Foo class:
public class Foo {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<Xref> xrefCollection;
}
Xref class:
public class Xref {
#Id
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "xrefId")
private Collection<Data> dataCollection;
#JoinColumn(name = "bar_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Bar barId;
#JoinColumn(name = "foo_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Foo fooId;
}
Data Class:
public class Data {
#Id
private Integer id;
#JoinColumn(name = "xref_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Xref xrefId;
}
This code has been automatically generated by NetBeans, provided that all tables and indexes are correctly defined in the DB

How to force OpenJPA (1.2.2) to eager load a #ManyToOne combined primary key?

i've got two Entities with combinded primarky keys:
#Entity
#IdClass(APK.class)
public class A {
#Id
Integer pk1;
#Id
Integer pk2;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER,mappedBy="a")
List<B> b = new ArrayList<B>();
String name;
...
}
#Entity
#IdClass(BPK.class)
public class B {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "pk1", referencedColumnName = "pk1"),
#JoinColumn(name = "pk2", referencedColumnName = "pk2")
})
#Id
private A a;
#Id
Integer additional_key_part;
}
When i load the "A" class, the List is correctly loaded eagerly, the problem is, it does not work when i load the "B" class.
The "B" class will correctly use an Join with A, but only the PK fields of A will be populated (pk1, pk2), not the rest.
The real problem comes when the entity is send to a client which has no transaction, so no lazy loading possible. The only thing that seemed to work is to call a.getName() or something to start the lazy loading before closing the transaction, but this seems not really the correct way.
So, how to make sure the entity and all its childs are loaded ?
Try changing the annotations to the following:
#Entity
#IdClass(ClassBPK.class)
public class ClassB {
#Id
private Integer pk1;
#Id
private Integer pk2;
#ManyToOne(fetch = FetchType.EAGER)
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "pk1", referencedColumnName = "pk1"),
#PrimaryKeyJoinColumn(name = "pk2", referencedColumnName = "pk2")
})
private ClassA a;
#Id
private Integer pk3;
//...
}
I run a quick check on OpenJPA 1.2.2 with Derby with a draft DAO class:
logger.debug("Pre find");
ClassB b = bDAOBean.findById(bId);
logger.debug("Post find");
logger.debug("A's name: {}", b.getA().getName());
and I got:
[DEBUG] Pre find
[INFO ] openjpa.Runtime - Starting OpenJPA 1.2.2 {main}
// ... Here comes SQL from loading the entity
[DEBUG] Post-find
[DEBUG] A's name: nameA11
There are no OpenJPA logs between the last two log lines, so it proves the whole ClassA was eagerly loaded.

Can add extra field(s) to #ManyToMany Hibernate extra table?

I have these two class(table)
#Entity
#Table(name = "course")
public class Course {
#Id
#Column(name = "courseid")
private String courseId;
#Column(name = "coursename")
private String courseName;
#Column(name = "vahed")
private int vahed;
#Column(name = "coursedep")
private int dep;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "student_course", joinColumns = #JoinColumn(name = "course_id"), inverseJoinColumns = #JoinColumn(name = "student_id"))
private Set<Student> student = new HashSet<Student>();
//Some setter and getter
and this one:
#Entity
#Table(name = "student")
public class Student {
#Id
#Column(name="studid")
private String stId;
#Column(nullable = false, name="studname")
private String studName;
#Column(name="stmajor")
private String stMajor;
#Column(name="stlevel", length=3)
private String stLevel;
#Column(name="stdep")
private int stdep;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "student_course"
,joinColumns = #JoinColumn(name = "student_id")
,inverseJoinColumns = #JoinColumn(name = "course_id")
)
private Set<Course> course = new HashSet<Course>();
//Some setter and getter
After running this code an extra table was created in database(student_course), now I wanna know how can I add extra field in this table like (Grade, Date , and ... (I mean student_course table))
I see some solution but I don't like them, Also I have some problem with them:
First Sample
If you add extra fields on a linked table (STUDENT_COURSE), you have to choose an approach according to skaffman's answer or another as shown bellow.
There is an approach where the linked table (STUDENT_COURSE) behaves like a #Embeddable according to:
#Embeddable
public class JoinedStudentCourse {
// Lets suppose you have added this field
#Column(updatable=false)
private Date joinedDate;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="STUDENT_ID", insertable=false, updatable=false)
private Student student;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="COURSE_ID", insertable=false, updatable=false)
private Course course;
// getter's and setter's
public boolean equals(Object instance) {
if(instance == null)
return false;
if(!(instance instanceof JoinedStudentCourse))
return false;
JoinedStudentCourse other = (JoinedStudentCourse) instance;
if(!(student.getId().equals(other.getStudent().getId()))
return false;
if(!(course.getId().equals(other.getCourse().getId()))
return false;
// ATT: use immutable fields like joinedDate in equals() implementation
if(!(joinedDate.equals(other.getJoinedDate()))
return false;
return true;
}
public int hashcode() {
// hashcode implementation
}
}
So you will have in both Student and Course classes
public class Student {
#CollectionOfElements
#JoinTable(
table=#Table(name="STUDENT_COURSE"),
joinColumns=#JoinColumn(name="STUDENT_ID")
)
private Set<JoinedStudentCourse> joined = new HashSet<JoinedStudentCourse>();
}
public class Course {
#CollectionOfElements
#JoinTable(
table=#Table(name="STUDENT_COURSE"),
joinColumns=#JoinColumn(name="COURSE_ID")
)
private Set<JoinedStudentCourse> joined = new HashSet<JoinedStudentCourse>();
}
remember: #Embeddable class has its lifecycle bound to the owning entity class (Both Student and Course), so take care of it.
advice: Hibernate team suppports these two approachs (#OneToMany (skaffman's answer) or #CollectionsOfElements) due some limitations in #ManyToMany mapping - cascade operation.
regards,
The student_course table is there purely to record the association between the two entities. It is managed by hibernate, and can contain no other data.
The sort of data you want to record needs to be modelled as another entity. Perhaps you could a one-to-many association between Course and StudentResult (which contains the grade, etc), and then a many-to-one association between StdentResult and Student.
Drop the many-to-many, create a class called StudentCourseRelationship and set up one to manys on Student and Course to the StudentCourseRelationship.
You can put all sorts of things on it, like DateEnrolled, DateKickedOut etc. etc.
IMO the many-to-many mapping is a bit of a con.
The accepted answer unfortunately doesn't work for me, hibernate generates the join table in a weird way (all join columns are duplicated). However the variant with dedicated entity for the join table works fine. Here it is described in great detail: http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/

Categories