I have a database which stores clubs and teams.
A club has multiple teams and a team is part of only one club.
Is it possible to query all teams which are not in a specific club using
the hibernate criteria api?
Here are the (stripped) Club.java and Team.java
Club.java
#Entity
#Table(name = "clubs")
public class Club {
#Id
#GeneratedValue
#Column(name = "club_id")
private int id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "club")
private java.util.Set<Team> teams;
[setters, constructors etc]
}
Team.java
#Entity
#Table(name = "team")
public class Team {
#Id
#GeneratedValue
#Column(name = "team_id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "club_id")
private Club club;
[same here]
}
As a slight variation on the answer above, if you already have the Club object which you wish to find all teams not belonging to it, you should be able to do something like:
Criteria criteria = session.createCriteria(Team.class);
criteria.add(Restrictions.ne("club", myClub));
List<Team> teams = criteria.list();
This avoids having to query on the club name and IMHO is cleaner code.
It should be something like this:
Criteria criteria=session.createCriteria(Team.class);
.createAlias("club", "c")
.add( Restrictions.not(Restrictions.like("c.name", "nameOfTheClubIAmLookingFor", MatchMode.EXACT)) )
List<Team> teams=criteria.list();
Related
I have two entities called Student and Subject. They are stored in tables in the following format
student_id
name
grade
1
John
1
subject_id
name
1
English
2
Math
subject_id
student_id
mark
1
1
75
2
1
75
**Student:**
#Table(name = "student")
#Data
public class Student {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "grade")
private int grade;
//getters and setters left out for this example
}
**Subject:**
#Table(name = "subject")
#Data
public class Subject {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "name")
private String name;
//getters and setters left out for this example
}
**StudentRepository:**
public interface StudentRepository extends JpaRepository<Student, Long> {
}
How do I make it so that everytime I add a student using a StudentController, the subjects are automatically added to the student.
Create the third entity for the third table, create the student object and the subject object . put it in the third entity object, create the third repository and save that, all three tables will be updated together. Just make sure your relationships are correctly mentioned and you are done.
Update your Student entity to encapsulate Subject.
The idea is to explicitly define relationship between Student and Subject , and further leverag Cascade to propagate changes :
#Table(name = "student")
#Data
public class Student {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "grade")
private int grade;
#OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE}, orphanRemoval = true)
#JoinColumn(name = "SUBJECT_ID")
Subject subject;
}
Note : You need to make sure that you populate Subject when storing Student.
For more clarity , explore the examples presented here : https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/
I have two table with many-to-one relationship. Example is, I have Office table and Employee table. One Employee belong to one Office and one Office belong to many Employee.
Office
#Entity(name = "office")
#Table(name = "office", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class Office {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "office_name", nullable = false)
private String officeName;
}
Employee
#Entity(name = "employee")
#Table(name = "employee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "employee_name", nullable = false)
private String employeeName;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "office_id", referencedColumnName = "id", nullable = false)
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private Office office;
}
OfficeDto
public class OfficeDto {
private Long id;
private String officeName;
}
EmployeeDto
public class EmployeeDto {
private Long id;
private String employeeName;
private OfficeDto office;
}
With above way of defining the entity and the DTO, when I do employee.findAll(), the JSON result is also include the detail of the office data.
Is there any way that I could achieve (objective):
When do saving new employee, I just have to mention the id of the office.
When do findAll employee, I could choose whether I want to gove the id only or also with the entire object to the client.
Because, with current situation, I think I need to define two employee DTO. First one is contain the entire office data (like the code of EmployeeDto) and the second one is replace private OfficeDto office with private int office.
The second problem you can solve by projection : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
Or just specific mapper to DTO, for mapping you can use mapstruct : http://mapstruct.org/documentation/installation/
For the first problem i found some answer in stack, but you need verify it : JPA many-to-one relation - need to save only Id
I've read many tutorials about spring-hibernate relationships but I'm a bit confused about how to use them in my case... I've product/category entities defined as follow:
Product
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#Column
private int category;
.
.
.
Category
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#NotEmpty
#Column
#Size (max = 25)
private String name;
.
.
.
So, I'd like in the product list page, under the voice "category" would appear the category name, and in the product form the category list...
In my case a product fits only one category so if I'm right it should be a #ManyToOne but I don't know how to implement this... in my product database I've the categoryId field, but if I mark the category entity field as #OneToMany it will not be stored to the db...
EDIT
I've changed like this (as suggested):
Product.class
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#NotEmpty
#Column
#Size (max = 25)
private String name;
#Column
#Size (max = 255)
private String description;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
Category.class
#Entity
#Table(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#NotEmpty
#Column
#Size (max = 25)
private String name;
#Column
#Size (max = 255)
private String description;
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.EAGER, mappedBy = "category", cascade = CascadeType.ALL)
private Set<Product> products = new HashSet<Product>();
Controller
#RequestMapping(value = "/add/", method = RequestMethod.POST)
public String addProduct(
#ModelAttribute(value = "product") #Valid Product product,
BindingResult result, ModelMap model, Category category) {
if (result.hasErrors()) {
return "forms/productForm";
}
try {
category.addProduct(product);
product.setCategory(category);
// Add product to db
productService.addProduct(product);
} catch (Exception e) {
log.error("/add/---" + e);
return "redirect:/product/deniedAction/?code=0";
}
return "redirect:/admin/product/";
}
I also added a #initbinder on the product controller to translate the data from the product form string to Category... but now when I save a product it automatically saves a category instead of attach the existing selected one...
As the Product will have only one Category and Category will have a list of Products, you can relate these two by creating a Foreign Key in the Product table to refer to the primary key in the Category table:
Category Table: id, name, other fields...
Product Table: id, category_id (FK), and other fields.
And the mapping can be defined as below:
public class Category {
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category", cascade = CascadeType.ALL)
private Set<Product> products = new HashSet<Product>();
...
}
public class Product {
//Here JoinColumn states that this entity is the owner of the relationship
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
...
}
The mappedBy attribute tells Hibernate that the collection is a mirror image of the many-to-one association on the other side. Its like telling Hibernate that it should propagate changes made at the Product end of the association to the database, ignoring changes made only to the products collection that you have in the Category. Thus if we only call category.getProducts().add(product), no changes will be made persistent. As the association is bidirectional, you have to create the link on two sides, not just one.
For your convenience, you can add one addProduct method in the Category class to save the association:
public void addProduct(Product product) {
product.setCategory(this);
products.add(product);
}
You appear to have a one-to-many relationship between Category and Product (one category has many products)
In Java (and OO generally) you'd expect the Category class to contain a list of Products, so the Category can be said to 'own' products.
In SQL it's the other way round - you'd expect Product table to hold a foreign key reference to a Category, so here, the Product can be said to 'own' a Category.
Looks like your using JPA, so you could have something like this:
Category class:
#Entity
public class Category {
//other stuff...
#OneToMany(cascade=CascadeType.ALL, mappedBy="category")
private Set<Product> products;
}
Product class:
#Entity
public class Product {
//other stuff...
#ManyToOne
private Category category;
}
so you have this:
Product{
atributtes...
#ManyToOne
Category category; --so every product has a category
}
Category {
attributtes...
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="id_Product")
private List<Product> products;
}
try this, if not we can look another solution..
You are right, you should use #ManyToOne because "...a product fits only one category...".
In Product entity declare a Category field instead of int category and annotate it with #ManyToOne. Also add #JoinColumn to specify the name of product.category_id column in the database.
Product:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#ManyToOne
#JoinColumn(name = "category_id")
private Category category;
.
.
.
I have 3 tables, each mapped to an entity. The entities are something like this:
#Entity
#Table(name = "person")
public class Person implements Serializable {
private int id;
//other fields
}
#Entity
#Table(name = "phone")
public class Phone implements Serializable {
private int id;
private Long price;
#ManyToOne
#JoinColumn(name = "personId")
private Person person;
#ManyToOne
#JoinColumn(name = "manufacturerId")
private Manufacturer manufacturer;
//other fields
}
#Entity
#Table(name = "manufacturer")
public class Manufacturer implements Serializable {
private int id;
private String name;
//other fields
}
What I want to do is to create a method that will return a list of Persons that have phones from a specified manufacturer with the price in a specified range.
EDIT: My dao class implements EntityJpaDao . I would need a solution that would work with this implementation.
Following query will return the Samsung mobile users with phone price range.
Criteria criteria = session.createCriteria(Phone.class, "phone");
criteria.createAlias("phone.person", "person")
criteria.add(Restrictions.between("phone.price", minPrice, maxPrice));
criteria.createAlias("phone.manufacturer","manufacturer");
criteria.add(Restrictions.eq("manufacturer.name", Samsung));
criteria.setProjection(Projections.property("person"));
List<Person> persons = criteria.list();
I have 2 tables. 1st one have oneToMany relationship with 2nd.
Class Author
#Entity
#Table(name = "Author")
Public class Author{
#Id
#Column(name = "AuthorId")
private int autherId;
#Column(name = "AuthorName")
private String authorName;
#OneToMany
#JoinColumn(name="AuthorId",referencedColumnName="AuthorId")
List<Book> Books;
//getter and setter
}
Class Book
#Entity
#Table(name = "Book")
Public class Book{
#Id
#Column(name = "BookId")
private int bookId;
#Column(name = "BookName")
private String bookName;
#Column(name = "AuthorId")
private int authorId;
//getter and setter
}
How can I write a Hql query so that I will get all author's and there books , with a condition that book name should starts with hello
I know using a query like this,
from Author;
I can fetch all author's and there books,but how to give condition on book?
I think its something like this:
select a from Author as a join a.Book as ab where ab.AuthorId like '%"hello"%';
not sure about a.Book though, it could also be a.Books as your columnname is named like that.