How to make a join on Java with criteria builder - java

I have a database in which I need to make a Join from Java with a CriteriaBuilder.
I have this code so far:
CriteriaBuilder cb = entman.getCriteriaBuilder();
CriteriaQuery<Company> query = cb.createQuery(Company.class);
Root<Employee> teacher = query.from(Employee.class);
Join<Employee, Company> employees = teacher.join("id");
query.select(employees).where(cb.equal(teacher.get("name"), ""));
List<Company> results = entman.createQuery(query).getResultList();
return results;
After I run this code ( with springboot) i get this error: Cannot join to attribute of basic type
Does anyone know what should I do to make it work ?
PS: I will provide any other information if needed.
Thanks in advance.
company database
employee database
L.E.:
Employee:
#Entity
public class Employee {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
#Size(min = 1)
private String name;
#Column(nullable = false)
#Temporal(TemporalType.DATE)
private Date hire_date;
#ManyToOne
//#JoinColumn(name = "id")
private Company company;
public Employee() {}
public Employee(Long id, String name, Date date, Company company) {
setId(id);
setName(name);
setHire_date(date);
setCompany(company);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getHire_date() {
return hire_date;
}
public void setHire_date(Date hire_date) {
this.hire_date = hire_date;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Company:
#Entity
public class Company {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
#Size(min = 1)
private String name;
#OneToMany(mappedBy="company", cascade = CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval=true)
private Collection<Employee> employees;
public Company() {}
public Company(Long id, String name) {
setId(id);
setName(name);
}
public Company(Long id, String name, Collection<Employee> employees) {
setId(id);
setName(name);
setEmployees(employees);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Employee> getEmployees()
{
return employees;
}
public void setEmployees(Collection<Employee> employees) {
this.employees = employees;
}
#Override
public String toString() {
return "Company [id=" + id + ", name=" + name + ", employees=" + employees.toString() + "]";
}
}

You need to have a Teacher element, not a reference to id because you cannot join a #Column field...
Change id mapping from #Column:
#Column(name = "id")
private Integer id;
with a #ManyToOne (or the needed one) association:
#ManyToOne
#JoinColumn(name = "id")
private Teacher teacher;
After this your join will work as expected.

Related

Spring- Postman too many information with ManyToMany

I have a problem with my Spring application- am practicing ManyToMany Relationship.
I made two Entity- student, and groups for them- everything is good but when am trying to display them in postman i have spaghetti result like :
{"id":4,"lastName":"guzik","groups":[{"id":1,"groupName":"grupka","students":
....(shorter version, its about 1000 lines)
[{"id":1,"lastName":"smith","groups":[{}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}{"timestamp":"2022-04-25T08:25:31.387+00:00","status":200,"error":"OK","path":"/api/student/4"}
What am doing wrong ?
enter code here
#RestController
#RequestMapping("/api")
public class MainController{
#Autowired
GroupRepo groupRepo;
#Autowired
StudentRepo studentRepo;
#GetMapping("/student/{id}")
ResponseEntity<?> getStudent(#PathVariable long id){
Optional<Students> student=studentRepo.findById(id);
return ResponseEntity.ok().body(student);
}
#GetMapping("/group/{id}")
ResponseEntity<?> getGroup(#PathVariable long id){
Optional<Groupssss> group=groupRepo.findById(id);
return ResponseEntity.ok().body(group);
}
#GetMapping("/student/{id}/groups")
ResponseEntity<?> studentGroups(#PathVariable long id){
Students student=studentRepo.findById(id).orElseThrow(()-> new UsernameNotFoundException("student not found"));
return ResponseEntity.ok().body(student.getGroups());
}
#PostMapping("/add")
#Transactional
ResponseEntity<?> addStudentToGroup(#RequestHeader long id) throws Exception{
Students student=studentRepo.findById(id)
.orElseThrow(()-> new UsernameNotFoundException("student not found"));
Groupssss group=groupRepo.findByGroupName("grupka").orElseThrow(Exception::new);
student.addGroup(group);
studentRepo.save(student);
return ResponseEntity.ok().build();
}
#Entity
public class Groupssss {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String groupName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "groups")
private Set<Students> students=new HashSet<>();
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setStudents(Set<Students> students) {
this.students = students;
}
public Set<Students> getStudents() {
return students;
}
public String getGroupName() {
return groupName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
#Entity
#Table(name="students")
public class Students {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String firstName;
String lastName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name="student_group",
joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="group_id"))
Set<Groupssss> groups=new HashSet<>();
public Students(){}
public Students(String firstName, String lastName){
this.firstName=firstName;
this.lastName=lastName;
}
public void addGroup(Groupssss group){
this.groups.add(group);
group.getStudents().add(this);
}
public Set<Groupssss> getGroups() {
return groups;
}
public String getLastName() {
return lastName;
}
public String getName() {
return firstName;
}
public void setGroups(Set<Groupssss> groups) {
this.groups = groups;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setName(String firstName) {
this.firstName = firstName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
If you use ManyToMany relationship, It calls the Parent and the Child entity recursively when you try to get List of it. There must be ignorance in child entity like:
In Parent entity you could call #JsonManagedReference
In Child entity you could call #JsonBackReference
Documentation is here:
Bidirectional Relationships
public class User {
public int id;
public String name;
#JsonManagedReference
public List<Item> userItems;
}
public class Item {
public int id;
public String itemName;
#JsonBackReference
public User owner;
}
Or you can use #JsonIgnore on top of the Parent declaration in Child entity. Some of the case it gets worst when you use JsonIgnore.
Documentation is here:
Jackson Ignore Properties
public class MyDto {
private String stringValue;
#JsonIgnore
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
Or use #JsonIgnoreProperties when you need to ignore many fields
#JsonIgnoreProperties(value = { "intValue" })
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
!Enjoy

Criteria query returning infinite nested result

I have 3 entities Movie, Show and Theatre with below relationship
Relations
#Entity
#Table(name = "theatre")
public class Theatre {
#Id
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "town")
private String town;
#OneToMany(mappedBy = "theatre", orphanRemoval = true)
private List<Show> shows = new ArrayList<>();
public List<Show> getShows() {
return shows;
}
public void setShows(List<Show> shows) {
this.shows = shows;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#Entity
#Table(name = "show")
public class Show {
#Id
#Column(name = "id", nullable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "theatre_id")
private Theatre theatre;
#ManyToOne
#JoinColumn(name = "movie_id")
private Movie movie;
public Movie getMovie() {
return movie;
}
public void setMovie(Movie movie) {
this.movie = movie;
}
public Theatre getTheatre() {
return theatre;
}
public void setTheatre(Theatre theatre) {
this.theatre = theatre;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#Entity
#Table(name = "movie")
public class Movie {
#Id
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "movie", orphanRemoval = true)
private List<Show> shows = new ArrayList<>();
public List<Show> getShows() {
return shows;
}
public void setShows(List<Show> shows) {
this.shows = shows;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Now when I try to fetch list of Theatres for a movie name I'm getting infinite nested result. As a result I'm getting StackOverflow error as well.
Is criteria query not suitable here? Or the relationship is wrong? Or criteria query is wrong itself.
Criteria query
public List<Theatre> findTheatresByMovieAndDate(String movieName) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Theatre> query = builder.createQuery(Theatre.class);
Root<Theatre> fromTheatres = query.from(Theatre.class);
Join<Theatre, Show> shows = fromTheatres.join("shows");
Join<Show, Movie> movie = shows.join("movie");
List<Predicate> conditions = new ArrayList<>();
conditions.add(builder.equal(movie.get("name"), movieName));
TypedQuery<Theatre> typedQuery = entityManager.createQuery(query
.select(fromTheatres)
.where(conditions.toArray(new Predicate[] {}))
.orderBy(builder.asc(fromTheatres.get("id")))
.distinct(true)
);
return typedQuery.getResultList();
}
Thanks in advance

no setter methods in generated mapstruct implementation

I'm experimenting with mapstruct and follow this tutorial:
mapstruct tut
I have this entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_company")
#SequenceGenerator(name = "seq_company", allocationSize = 1)
#Column(nullable = false, updatable = false)
private Long id;
private String name;
private String shortName;
public Company() {
}
public Company(Long id, String name, String shortName) {
this.id = id;
this.name = name;
this.shortName = shortName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
And this is the simple dto:
public class CompanyDto {
#JsonProperty("id")
private Long id;
#JsonProperty("name")
private String name;
#JsonProperty("shortName")
private String shortName;
}
And here is the mapper interface:
#Mapper(componentModel = "spring")
public interface CompanyMapper {
CompanyDto companyToCompanyDto(Company company);
Company companyDtoToCompany(CompanyDto companyDto);
List<CompanyDto> companiesToCompanyDtos(List<Company> companies);
}
I certanly oversee something, because there is no setters in the generated implementation, f. e.:
#Override
public Company companyDtoToCompany(CompanyDto companyDto) {
if ( companyDto == null ) {
return null;
}
Company company = new Company();
return company;
}
What goes here wrong?
I've noticed that your CompanyDto class has private fields but no getters or setters. There is no standard way to access the fields in that class. You might need to add those in order to map in or out of that class.

issue with Criteria when making an association on 2 tables with a fereign key

I m trying to learn about hibernate and Criteria.
i have 2 tables RATS and SICKNESS.
I set a foreign key in RATS : RATS.Sickness_Id = SICKNESS.ID.
I m trying to get with Criteria an equivalent of SQL:
select * from RATS r, SICKNESS s where s.id = r.sickness_id
I assumed it was this association:
session
.createCriteria(Rats.class)
.createCriteria(Sickness.class)
.toList()
This unfortunately ends up with:
org.hibernate.QueryException: could not resolve property: entities of: entities.Rats
Strange part is that both:
session.createCriteria(Rats.class).toList()
and
session.createCriteria(Sickness.class).toList()
work fine....
I'm a bit puzzled.
Here are my entities classes code:
#Entity
#Table(name = "RATS")
public class Rats implements java.io.Serializable {
private int id;
private Sickness sickness;
private String name;
private int age;
public Rats() {
}
public Rats(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Rats(int id, Sickness sickness, String name, int age) {
this.id = id;
this.sickness = sickness;
this.name = name;
this.age = age;
}
#Id
#Column(name = "ID", unique = true, nullable = false)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Sickness_Id")
public Sickness getSickness() {
return this.sickness;
}
public void setSickness(Sickness sickness) {
this.sickness = sickness;
}
#Column(name = "Name", nullable = false, length = 50)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "Age", nullable = false)
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
#Override
public String toString() {
String returnString = "My name is " + getName() + ", I am " + getAge()+ ". ";
returnString += getSickness() == null ? "I am healthy like hell! :)" : "I suffer from " + getSickness().getNom();
return returnString;
}
}
and
#Entity
#Table(name = "SICKNESS")
public class Sickness implements java.io.Serializable {
private int id;
private String nom;
private Set<Rats> ratses = new HashSet<Rats>(0);
public Sickness() {
}
public Sickness(int id) {
this.id = id;
}
public Sickness(int id, String nom, Set<Rats> ratses) {
this.id = id;
this.nom = nom;
this.ratses = ratses;
}
#Id
#Column(name = "Id", unique = true, nullable = false)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "Nom", length = 50)
public String getNom() {
return this.nom;
}
public void setNom(String nom) {
this.nom = nom;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "sickness")
public Set<Rats> getRatses() {
return this.ratses;
}
public void setRatses(Set<Rats> ratses) {
this.ratses = ratses;
}
#Override
public String toString() {
return getNom()
+ ( getRatses() != null ? (", getRatses()=" + getRatses() + "]"): "" );
}
}
What did I miss?
Thanks in advance.
On Rats entity, the Sickness entity property is:
private Sickness sickness;
Accordingly, your association must use the same name.
session.createCriteria(Rats.class)
.createCriteria("sickness")
.list();
One other solution, should be to change Rats to use EAGER Fetch:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Sickness_Id")
public Sickness getSickness() {
return this.sickness;
}
public void setSickness(Sickness sickness) {
this.sickness = sickness;
}
and then use:
session.createCriteria(Rats.class)
.list();

Retrieve data using hsql - relation many to one

I define the following entities :BaseEntity , magasin and article :
#Entity(name = "magasin")
#Table(name = "magasin")
public class Magasin extends BaseEntity {
private static final long serialVersionUID = 1L;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#OneToMany(cascade=CascadeType.ALL, mappedBy="magasin")
#Valid
private Set<Article> article;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Article> getArticle() {
return article;
}
public void setArticle(Set<Article> article) {
this.article = article;
}
}
#Entity(name="article")
#Table(name="article")
public class Article extends BaseEntity {
private static final long serialVersionUID = 1L;
#ManyToOne
private Magasin magasin;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#Basic
private float price;
public Magasin getMagasin() {
return magasin;
}
public void setMagasin(Magasin magasin) {
this.magasin = magasin;
}
public String getName() {
return name;
}
public void setName(String nom) {
this.name = nom;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public boolean isNew() {
return (this.id == null);
}
}
How can create a hql query in order to retrieve all Article for a magasin selected ?
I try this
#Override
public List<Article> findArticle(Magasin magasin) {
String query = "From Article m where m.magasin.id = "+magasin.getId();
System.out.print(query);
Session session = getSessionFactory().getCurrentSession();
if((session.createQuery(query).list()!=null) && (session.createQuery(query).list().size()!=0))
return (List<Article>) session.createQuery(query).list();
else
return null;
}
But it returns nothing , always null .How can I resolve it ?
I don't know the type of your magasin id so adapt the code below.
First get the Magasin instance for the id:
Magasin mag = (Magasin)session.get(Magasin.class, id);
Next you can access the articles for the magasin mag via accessor
Set<Article> articles = mag.getArticle();
Try this:
"Select * From Article,Mgasin where Article.mgasin.id = "+magasin.getId();

Categories