Specifications JPA - Inner + where clause - java

I have to join two tables and filter the result with a Where with Specifications and JPA. But I'm not very familiar with it.
The tables are for example:
public class A {
private Long id;
private Long secondId;
...
}
public class daughterA {
#JoinColumn (name = "id")
private Long idA;
...
}
and the WHERE is to be applied on secondId.
Thank you all for the help, I apologize for any inaccuracies, i'm beginning with these technologies.

First of all, your Join seems to be a one To Many join so you should make it as it is :
add the One annotation in the parent class being mapped by the name of the instance of it in the other class, this notation covers a Set or a List of objects of the daughters class
add the ManyToOne annotation in the daughter class, a daughter can only have one mother, and idA is not Long but it refers to the parent class it's in fact a foreign key
public class A{
#Id
private Long id;
private Long secondId;
#OneToMany(mappedBy="idA")
     private Set<daughterA > daughters;
...
}
public class daughterA {
#ManyToOne
#JoinColumn (name = "id")
private A idA;
...
}
and for the where clause :
TypedQuery<Country> query =
em.createQuery("SELECT d FROM daughterA d WHERE d.idA.secondId = :secondId ", Country.class);
A results = query.setParameter("secondId",sencondId).getSingleResult();

Related

Java entity modeling with table that relates to several others

I have a doubt about how the modeling of my entity would be. Come on, I have a table in the database that serves to save documents from my system, this table has the columns id, fk_id (element foreign key), fk_table (entity name) and file_name (stores the name of my file) .
I did a lot of research before posting my question here, but I didn't find anything related to it, what would my entities, user, patient and doctor?
DB:
id
fk_id
fk_table
file_name
1
21
user
test1.jpg
2
32
doctor
test2.pdf
3
61
user
test10.pdf
4
100
patient
test5.jpg
Class:
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String LastName;
// What would a one-to-many relationship look like?
}
public class patient{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
// What would a one-to-many relationship look like?
}
You can use #Where. But be aware that #Where is a Hibernate annotation. It's not in the JPA standard.
For example in the User entity: (I assume that your table is mapped to an entity called Document)
#Where( clause = "fk_table = 'user'")
#JoinColumn(name = "fk_id")
#OneToMany
private List<Document> documents = new ArrayList<>( );
The following is based only on standard JPA annotations. The idea is to create an inheritance hierarchy for the documents table. The base is:
#Entity
#Table(name = "XX_DOCUMENT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "fk_table")
public abstract class BaseDocument {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#Column(name = "file_name")
private String fileName;
}
Here we define that all entities extending this will go to the same table, with the fk_table column to discriminate. The entities extending it are defined as follows:
#Entity
#DiscriminatorValue("doctor")
public class DoctorDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Doctor doctor;
}
#Entity
#DiscriminatorValue("patient")
public class PatientDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Patient patient;
}
// and so on
The interesting thing is that we are reusing the column fk_id to point to the right table. From a small experiment, Hibernate seems to not have problems with it. I would suggest that you manage the DB creation another way just to be safe.
The Doctor, Patient etc need not have a common base class, e.g.:
#Entity
#Table(name = "XX_DOCTOR")
public class Doctor {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "doctor")
private Collection<DoctorDocument> documents = new ArrayList<>();
// any doctor-specific fields
}
#Entity
#Table(name = "XX_PATIENT")
public class Patient {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "patient")
private Collection<PatientDocument> documents = new ArrayList<>();
// any patient-specific fields
}
// and so on
You can read a (doctor, patient, ...)'s documents from the relevant collection. You can even query BaseDocument instances based on any criteria.
You can even go ahead and do more fabcy stuff with the Java code. E.g. define an interface HasDocuments:
public interface HasDocuments<D extends BaseDocument> {
Collection<D> getDocuments();
}
Doctor, Patient, ..., implements this, so they can all be treated the same way.

DB column value not saving with primary key value after using #PostPersist annotation

I'm having three entities which are being saved into the DB. For instance we have 3 entities A, B and C. A is having OneToOne mapping with B and ManyToOne mapping with C which is uni-directional.
public class A {
#Id
#GeneratedValue
private int id;
private String number;
//Other fields declared
#PostPersist
private void onSaving(){
number = "STU" + id;
}
}
public class B {
#Id
#GeneratedValue
int otherID; //primary key for B
//Other fields declared
#OnetoOne(targetEntity = A.class, cascade= CascadeType.ALL)
#JoinColumn(name = "id") // id is the primary key of class A
private A a;
}
public class C {
#Id
#GeneratedValue
int otherID; //primary key for C
//Other fields declared
#ManytoOne
#JoinColumn(name = "id") // id is the primary key of class A
private A a;
}
Below is the service class for the above entities
#Service
public class testService {
#Autowired
private BRepo bRepo; // BRepo is the interface which is extending the JPARepository.
#Autowired
private CRepo cRepo; // CRepo is the interface which is extending the JPARepository.
#Transactional
public A saveEntity(B b, List<C> c) {
bRepo.save(b);
cRepo.saveAll(c);
return b.getA();
}
}
The entity fields/data are getting perfectly saved into the DB, the only issue I'm facing is with the #PostPersist annotation. My requirement is when save is performed, the number field/variable of entity A should be saved as "STU" + id(primary key for A), but in the DB it is saving as STU0 every time I perform save.
I'm unable to find the root cause for this over the internet. Requesting you to please help me debug this issue. Thank you.
The issue got resolved after I added, strategy as IDENTITY
#GeneratedValue(strategy = GenerationType.IDENTITY)

How to map a one-many relationship that has an attribute (Hibernate)

I have 2 entities O and D with a one to many relationship from O (one) to D (many). The relationship itself has an attribute - count.
What is the best way to model this using hibernate?
What I have currently is another entity OD representing the relationship. It has its own artificial key
The abbreviated version of the entities is as below
#Entity
class O {
#Id
private Long id;
#OneToMany(mappedBy = "o")
private Set<OD> ods;
}
#Entity
class OD {
#Id
private Long id;
#ManyToOne
private O o;
// This is uni-directional reln
#OneToOne
private D d;
private int count;
}
Is this the best way? I do not like the fact that the relationship has its own id, but is there a better way to model this relationship?
You can model the OD as #Embeddable, and change the owning side from #OneToMany to #ElementCollection e.g.
#Entity
public class O {
#Id
private Long id;
#ElementCollection
#CollectionTable( name = "OD")
private Set<OD> ods;
}
#Embeddable
public class OD {
// This is uni-directional reln
#OneToOne
private D d;
private int count;
}
The DDL statements would be almost the same with the distinction that the life-cycle of the OD would always be dependent on the life of its parent object, and would not hold an identity of its own

setting an id autogenerate into an object

Sorry if my post is duplicated or the tittle doesn't describe the topics, because I don't know how to describe this in the tittle, I look on internet, but I didn't find the solution.
I am using Java and JPA. The problem is the next :
I have a class A with an autogenerated key :
class A{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private List<B> listB;
}
And the class B with the id of this clas:
class B {
#EmbeddedId
private Bid id;
private String att;
}
class Bid {
private int idA;
private String text;
}
In a controller I want to create an object A, the problem is when I created the object A, I need to create the object B where the id of B contains the id of A which is autogenerated, and it is created in the moment when the entity is mapped to de database, I dont't know how to set the id autogenerated of A into the idB, maybe I should query to de database asking what is the las id of classA, but it seem bad.
Thanks in advance
Your case is a derived identifier case, where your entity B's identity was derived from the primary key of A. You can use #MapsId annotation for this case and your entities can be restructured like this:
#Entity
public class A {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#OneToMany(mappedBy="a")
private List<B> listB = new ArrayList<B>();
...
}
#Entity
public class B {
#EmbeddedId
private BId id;
#ManyToOne
#MapsId("idA")
private A a;
...
}
#Embeddable
public class BId {
private int idA;
private String att;
...
}
This is how you would persist the entities:
A a = new A();
BId bid = new BId();
bid.setAtt("text"); // notice that the idA attribute is never manually set, since it is derived from A
B b = new B();
b.setId(bid);
b.setA(a);
a.getListB().add(b);
em.persist(a);
em.persist(b);
See sample implementation here.
It would be useful to know which is the case scenario you are trying to solve in general because the structure you are using seems unnecessarily complex.
What is your real goal?

Hibernate criteria on embedded id member member value

I would like to find an entity using a critera with restriction on the value of an attribute of a second entity wich is a member of the embedded id of my first entity.
First entity :
#Entity
public class Car {
#EmbeddedId
private Id id = new Id();
private String color;
#Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = -8141132005371636607L;
#ManyToOne
private Owner owner;
private String model;
// getters and setters...
// equals and hashcode methods
}
// getters and setters...
}
Second entity :
#Entity
public class Owner {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
#OneToMany (mappedBy = "id.owner")
private List<Car> cars;
// getters and setters...
}
In this example, I would like to obtain the car with the color 'black', model 'batmobile' and the owner's firstname 'Bruce' (oops... spoiler ;) )
I tried to do something like that but it won't work :
List<Car> cars = session.createCriteria(Car.class)
.add(Restrictions.eq("color", "black"))
.add(Restrictions.eq("id.model", "batmobile"))
.createAlias("id.owner", "o")
.add(Restrictions.eq("o.firstname", "Bruce"))
.list();
Result :
Hibernate: select this_.model as model1_0_0_, this_.owner_id as owner_id3_0_0_, this_.color as color2_0_0_ from Car this_ where this_.color=? and this_.model=? and o1_.firstname=?
ERROR: Unknown column 'o1_.firstname' in 'where clause'
What is the right way to obtain what I want ?
update
I tried in hql :
String hql = "FROM Car as car where car.color = :color and car.id.model = :model and car.id.owner.firstname = :firstname";
Query query = em.createQuery(hql);
query.setParameter("color", "black");
query.setParameter("model", "batmobile");
query.setParameter("firstname", "Bruce");
List<Car> cars = query.getResultList();
It works but is there a way to do this with criteria ?
You forgot to add the #Column annotation on top of the firstname and lastname fields (and the color field in Car). In hibernate if a field is not annotated, it doesn't recognize it as a database field. This page should give you a good idea about how to set up your model objects.
NOTE: You can have the column annotation over the getters and be fine, but you didn't show the getters. Either place is fine.
Look at what HQL is spitting back out, specifically the statement (formated for easier reading):
select
this_.model as model1_0_0_,
this_.owner_id as owner_id3_0_0_,
this_.color as color2_0_0_
from Car this_
where
this_.color=?
and this_.model=?
and o1_.firstname=?
It looks like hibernate is translating the field "id.owner" to "o" as your alias told it to to, but for some reason it's not writing down that "id.owner=o" as intended. You may want to do some research into why it may be doing that.
As per https://hibernate.atlassian.net/browse/HHH-4591 there is a workaround.
You have to copy the needed relation-property of the #EmbeddedId (owner in this case) to the main entity (Car in this case) with insertable = false, updatable = false as follows
#Entity
public class Car {
#EmbeddedId
private Id id = new Id();
private String color;
#ManyToOne
#JoinColumn(name = "column_name", insertable = false, updatable = false)
private Owner owner;
#Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = -8141132005371636607L;
#ManyToOne
private Owner owner;
private String model;
// getters and setters...
// equals and hashcode methods
}
// getters and setters...
}
Then just create directly the alias instead of using the composite id property
List<Car> cars = session.createCriteria(Car.class)
.add(Restrictions.eq("color", "black"))
.add(Restrictions.eq("id.model", "batmobile"))
.createAlias("owner", "o")
.add(Restrictions.eq("o.firstname", "Bruce"))
.list();

Categories

Resources