how to select child in one to many relation in jpa - java

I want to select parent with child that i want.
But when I select my parent I have to show all the childs
How can i do that?
Example:
public class parent{
private Integer id;
#OnetoMany
#JoinColumn(name="parentId")
private List<child> children;
}
public class child{
private Integer id;
private Integer parentId;
}
findByIdAndchildType(Integer id, String type)
I want to see : parent(id) - > child (type)
But i can see parent(id) - > child(othertype), child(othertype1), child(type)

It sounds to me that you're trying to get a bi-directional relation. This is possible by adding the mapping to both sides of the relation.
For example, add a #ManyToOne mapping to the Child entity. Be aware that you should probably remove your parentId field since now you can access it by using child.getParent().getId().
#Entity
public class Child {
#Id
private Integer id;
#ManyToOne
#JoinColumn(name = "parentId")
private Parent parent;
// Remove parentId field
// Getters + Setters ...
}
NOTE: If you want to keep the parentId field, you'll have to choose which two of the mappings (getParentId() or getParent().getId()) you want to use for inserting and updating entities. The other field should have both insertable = false and updatable = false.
The next step is to change the #OneToMany mapping to use mappedBy:
#Entity
public class Parent {
#Id
private Integer id;
#OneToMany(mappedBy = "parent") // Change this
private List<Child> children;
// Getters + Setters ...
}
If you want to retrieve a specific child with its parent, you can now create a repository for Child entities:
public interface ChildRepository extends JpaRepository<Child, Integer> {
}
After that, you can get a specific child by using:
Optional<Child> child = repository.findById(123); // 123 is the ID of the child in this case
Optional<Parent> parent = child.map(Child::getParent);
With Spring boot 1.x that would be:
Child child = repository.findOne(123);
Parent parent = null;
if (child != null) {
parent = child.getParent();
}

Related

Spring Boot JPA join columns with a partial composite key

I have entity classes like this structure:
Class Parent {
#EmbededId
private ParentId id;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="parentDetails")
private List<Child> childDetails;
...
}
#Embeddable
Class ParentId {
private Integer pid1;
private Integer pid2;
private Integer pid3;
...
}
Class Child {
#EmbededId
private ChildId id;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumns({
#JoinColumn(name="C_ID1" referencedColumnName="P_ID1")
#JoinColumn(name="C_ID2" referencedColumnName="P_ID2")
)}
private Parent parentDetails;
...
}
#Embeddable
Class ChildId {
private Integer cid1;
private Integer cid2;
private date cid3; // its a totally different field
...
}
I don't have any relationship with pid3 and cid3 as they are different. If I go with above design I am getting below error:
org.hibernate.AnnotationException: referencedColumnNames(P_ID1, P_ID2) of Child.parentDetails referencing Parent not mapped to a single property
If I comment pid3 then it works. So does that mean that I can't refer part of composite key as join columns? Is there any solution for it? I can't make changes to tables as they are legacy.

JPA Cannot access parent class from list of child class

I'm trying to understand jpa relationships.
I have a #OneToMany relationship between two classes.
public class Parent {
#Id
private Long id;
#OneToMany(mappedBy="parent", fetch=FetchType.LAZY)
private List<Child> children;
}
public class Child {
#Id
private Long id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id")
private Parent parent;
}
I'm would like to get the Parent Id from the list of Child but the Parent is null
List<Child> children = repository.findAll();
Parent parent = children.get(0).getParent(); //for testing, this is null
But when I only get one Child, the Parent is not null
Child child = repository.findOne();
Parent parent = child.getParent() // this is not null
I'm not sure why Parent is null when using findAll();

How to write a Repository method for a parent entity that returns child entities?

I am designing two entities, one called Country and one called CountryDetail. From the perspective of tables, the COUNTRY table will be the parent table, and the COUNTRY_DETAIL table will be the child table. In the COUNTRY table, there will be a unique attribute called COUNTRY_CODE (note this is not a primary key; the primary key will be a numeric sequence based value). This code will be a foreign key to connect to the child table, and in this child table, each COUNTRY_CODE from the parent table will have 3 entries to represent the name of the country in 3 different languages. Following are the entity classes:
Country.java
#Entity
public class Country
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "COUNTRY_ID")
private long id;
#Column(name="COUNTRY_CODE", nullable = false, unique = true)
private String countryCode;
/*public getters*/
}
CountryDetail.java
#Entity
public class CountryDetail
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "COUNTRY_DETAIL_ID")
private long id;
#ManyToOne
#JoinColumn(name="COUNTRY_CODE", referencedColumnName = "COUNTRY_CODE")
private Country country;
#Column(nullable = false)
private String languageCode;
#Column(nullable = false, unique = true)
private String countryNameInLanguage;
/*public getters*/
}
My question is, how can I write a custom "findBy..." interface method inside an extension of JpaRepository that is typed to a Country that would return me a collection of CountryDetail elements that match an input parameter for the languageCode attribute of the CountryDetail class?
public interface CountryRepository extends JpaRepository<Country, Long>
I know how to do it if the repository was typed to CountryDetail instead of Country, but I would like to know how to do it going via the parent entity rather than via the child entity directly, even though the input parameter (languageCode) exists only in the child entity.
Thank you.
It is possible but complicated : Please see an example below :
Parent :
#Entity
#Table(name="PARENT")
public class Parent {
#Id
#Column(name="PARENT_ID")
private int parentId;
#Column(name="PARENT_NAME")
private String parentName;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Child child;
}
Child :
#Entity
#Table(name="CHILD")
public class Child {
#Id
#Column(name="CHILD_ID")
private int childId;
#Column(name="CHILD_NAME")
private String childName;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Parent parent;
}
Define an interface to load child entity from parent repository :
ChildEntity:
public interface ChildEntity {
#Value("#{target.child.childId}")
int getChildId() ;
#Value("#{target.child.childName}")
String getChildName();
}
Parent Repository :
public interface ParentRepository extends JpaRepository<Parent, Integer> {
public ChildEntity findByParentName(String parentName);
}
Test class:
ChildEntity chi=rep.findByParentName("<<NAME>>");
System.out.println(chi.getChildId()+" "+chi.getChildName());
Output:
CHILD ID 1000 CHILD NAME child1

Hibernate annotation mapping to joined table with composite key

I have an entity, that i'd like to join OneToOne with a table with a composite key (Omitting getters/setters):
#Entity
#Table(name = "parent")
public class Parent {
#Id
private String parentId;
#Column(name = "data")
private String data;
#OneToOne
private Child child;
}
And:
#Entity
#IdClass(ChildKey.class)
#Table(name = "child")
public class Child{
#Id
private String parentId;
#Id
private String username;
#Column(name = "data")
private String childData;
}
public class ChildKey implements Serializable {
private String parentId;
private String username;
}
Parent does not have a notion of the 'username' field in the Child entity. I need to pass this in as criteria. In the DB, the primary key of child is on parentId and username.
If I don't specify a JoinColumn, hibernate attempts to map using fields child_username and child_parentId. If I specify only one Joincolumn, I get a broken mapping. If I specify both JoinColumns, I have no column on parent to specify.
How can I map this class and pass in the username as criteria? (it is coming from authentication data) Or how can I do this in a different way if I'm off track.
You might be able to use a Derived Identity.
The Parent class would remain the same; but you would specify a #OneToOne mapping back to the child's parent and the Child and ChildKey classes would look like this:
#Entity
#IdClass(ChildKey.class)
#Table(name = "child")
public class Child{
#Id
#OneToOne(mappedBy="child")
private Parent parent;
#Id
private String username;
#Column(name = "data")
private String childData;
}
public class ChildKey implements Serializable {
private String parent; // name matches name of the #Id field and type matches type of Parent #Id field
private String username; // name and type match those of the #Id field
}
Derived identity is discussed in JPA 2.1 spec, section 2.4.1.
What I ended up doing was defining a #Filter on the Child class, like so:
#Entity
#IdClass(ChildKey.class)
#Table(name = "child")
#FilterDef(name = "usernameFilter", parameters = {
#ParamDef( name = "username", type="string")
})
public class Child { ... }
On the Parent class, I annotated the collection with a reference to the filter:
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "parentId")
#Filter(name="usernameFilter", condition = "username = :username")
private List<Child> children;
Finally, in my DAO, I parameterized the filter by name like so:
Filter filter = currentSession().enableFilter("usernameFilter");
filter.setParameter("username", user.getUsername());
Doing this resulted in the exact SQL I had in mind, which is an additional clause in the JOIN criteria with a variable:
SELECT
...
FROM
parent this_
LEFT OUTER JOIN
child child_ ON this_.parentId = child_.parentId
AND child_.username = ?
I might not have been clear about what end result I was looking for in my original question. Posting this answer in case it helps someone else.

Two almost identical OneToOne in Entity, custom join annotation

I've got Parent - Child relationship OneToOne, but two of them. Annotations are not good but it produces good DB schema, yet code is not working. If I try to save Parent instance, Hibernate at first tries to save child1 and child2 - but it breaks FK defined in Child -> because owner doesn't exist yet in DB...So I need to save Parent and then child1, and child2.
If I could do that it doesn't help, because when I try to load Parent, Hibernate will not know which record in Child table belongs to child1 or child2...So in this case I would need to specify one condition in join for child1 something like "where type = 1" and for child2 "where type = 2"...
Just to clarify: in Child table there will be zero or one child for one Parent with ChildType.A (always child1) and zero or one child with ChildType.B (always child2).
I need to save xml which looks like this:
<parent id="" oid="">
<child1 (and other attributes)>
<child2 (and other attributes)>
<parent>
Both child1 and child2 elements are the same type therefore are type of Child in java classes. Only difference is element name (in java I differentiate them with ChildType). Only identification for children are id and oid attributes from parent. They points to another Parent hence target in Child.
I need to change annotations somehow to get this working...Do you guys have some ideas, because I'm really stuck???
Parent.java
public class Parent {
private String oid
private Long id;
private Child child1;
private Child child2;
#Id
#GeneratedValue(generator = "IdGenerator")
#GenericGenerator(name = "IdGenerator", strategy = "com.example.IdGenerator")
#Column(name = "id")
public Long getId() {
return id;
}
#Id
#GeneratedValue(generator = "OidGenerator")
#GenericGenerator(name = "OidGenerator", strategy = "com.example.OidGenerator")
#Column(name = "oid", nullable = false, updatable = false, length = 36)
public String getOid() {
return oid;
}
#OneToOne(optional = true, fetch = FetchType.EAGER)
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild1() {
return child1;
}
#OneToOne(optional = true, fetch = FetchType.EAGER)
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild2() {
return child2;
}
}
Child.java
public class Child {
private Parent owner;
private String ownerOid;
private Long ownerId;
private ChildType type;
private Parent target;
#MapsId("owner")
#ManyToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "owner_oid", referencedColumnName = "oid"),
#PrimaryKeyJoinColumn(name = "owner_id", referencedColumnName = "id")
})
public Parent getOwner() {
return owner;
}
#MapsId("target")
#ManyToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "target_oid", referencedColumnName = "oid"),
#PrimaryKeyJoinColumn(name = "target_id", referencedColumnName = "id")
})
public Parent getTarget() {
return target;
}
#Id
#Column(name = "owner_id")
public Long getOwnerId() {
if (ownerId == null && owner != null) {
ownerId = owner.getId();
}
return ownerId;
}
#Id
#Column(name = "owner_oid", length = 36)
public String getOwnerOid() {
if (ownerOid == null && owner != null) {
ownerOid = owner.getOid();
}
return ownerOid;
}
#Id
#Column(name = "target_id")
public Long getTargetId() {
if (targetId == null && target != null) {
targetId = target.getId();
}
return targetId;
}
#Id
#Column(name = "target_oid", length = 36)
public String getTargetOid() {
if (targetOid == null && target != null) {
targetOid = target.getOid();
}
if (targetOid == null) {
targetOid = "";
}
return targetOid;
}
#Id
#Enumerated(EnumType.ORDINAL)
public ChildType getType() {
if (type == null) {
return ChildType.A;
}
return type;
}
}
ChildType.java
public enum ChildType {
A, B;
}
I also tried to use mappedBy approach mappedBy approach but there are still problems with loading - I can't tell hibernate which child record belogs to which child class member variable.
there are too many things I don't quite get in your solution to give a good answer but just some thoughts:
Consider using inheritance instead of ChildType enum. So you would have ChildA and ChildB extending Child.
That way you Parent can have:
private ChildA child1;
private ChildB child2;
Instead of having a composite primary key, I would consider using a unique auto generated key and then add a unique constraint on on the other id and oid fields. It should make the child parent relationships easier and you can have different parent implementation for ChildA and ChildB:
In ChildA:
#OneToOne(mappedBy="child1")
public Parent getParent() {
return parent;
}
And
In ChildB:
#OneToOne(mappedBy="child2")
public Parent getParent() {
return parent;
}
And in Child just:
public abstract Parent getParent();
Now the whole Parent/Owner/Target thing I still didn't quite grasp.
I see two problems with using one-to-one references:
Parent has zero or one child1, and zero or one child2. My understanding is that you use one to one when it's ALWAYS one.
Your IDs are getting really complicated. The idea with one-to-one is that the ID's are the same between the two entities, and you've got Parent with a two-part primary key, and Child with a different four part primary key.
The other addendum to 2. is that if you're using 1-1, then the ID of parent and child should be the same. But if the parent has two children, they can't both have the same ID! So there's a real data modeling problem. Also, the fact that child type is part of the key is also a bad smell, cause I expect child type is part of your business logic,
I think what you want is what Hibernate calls a 'unidirectional one-to-one association on a foreign key'. The Hibernate ORM manual shows how to do this in XML, here's an example with Annotations, although without the unique property set.
I'm going to assume there's no outside business reason why your primary keys have to be the way that they are, and suggest that you
Change Parent and Child to each have to have a single field primary key, using #Id and #GeneratedValue(strategy=GenerationType.AUTO).
Change Parent to have the references to child1 and child2 be #ManyToOne(unique=true)
If you want, change Child as detailed in the Hibernate manual so that it has a reference to the Parent.
You will lose the 'feature' that parents and children have the same ID, but gain massive simplicity in your object ID scheme, making database operations faster and the code simpler to read.
If there is an outside reason that you haven't specified as to why Parent and/or Child need multi-part primary keys, this answer will need some modification.
I also second the point that #barsju made about using inheritance, or even entirely independent classes, to do the two Child types. If you use ID's the way I lay out here, you can make Parent have references to the concrete subtypes, and the query will work fine.
Use #JoinColumns and define a filter
In Parent.java:
#OneToOne(optional = true, fetch = FetchType.EAGER)
#JoinColumns ({
#JoinColumn(name="id", referencedColumnName = "owner_id"),
#JoinColumn(name="oid", referencedColumnName = "owner_oid"),
})
#FilterJoinTable(name="type", condition="type = 'A'")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild1() {
return child1;
}
#OneToOne(optional = true, fetch = FetchType.EAGER)
#JoinColumns ({
#JoinColumn(name="id", referencedColumnName = "owner_id"),
#JoinColumn(name="oid", referencedColumnName = "owner_oid"),
})
#FilterJoinTable(name="type", condition="type = 'B'")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild2() {
return child2;
}
I never used filters, so the exact syntax may be a little bit different, but that should give you the idea how it should work.
By the way, you can make the design easier if you introduce a single id column (not a composite id as you do it now). In that case the type don't need to be part of the id. Hibernate does not really get happy with composite ids. Nevertheless composite ids work and they don't have anything to do with your problem. The filter should work in the same way then.

Categories

Resources