Spring JPA how can I retrieve join table with a given property? - java

I am working on a Spring project and I am using JPA to access database. I have two tables that have #ManyToMany property. And one other table (join table) that keeps the foreign keys of those two tables. I need to retrieve the join table with a given property of one of the tables. I tried to use JPA methods and queries but I couldn't find a solution.
I have an abstract class called Course:
#Data
#NoArgsConstructor
#MappedSuperclass
public abstract class Course {
#Id
#GeneratedValue(generator = "UUID")
#Column(name = "id", nullable = false)
private UUID courseId;
#NotBlank
#Column(name = "course_code", nullable = false)
private String courseCode;
#NotBlank
#Column(name = "course_name", nullable = false)
private String courseName;
#NotNull
#Column(name = "ects", nullable = false)
private Double ects;
public Course(
#JsonProperty("id") UUID id,
#JsonProperty("courseCode") String courseCode,
#JsonProperty("courseName") String courseName,
#JsonProperty("ects") Double ects) {
this.courseId = id;
this.courseCode = courseCode;
this.courseName = courseName;
this.ects = ects;
}
}
I have two database model classes that extends Course:
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Table(name = "uni_course")
public class UniCourse extends Course {
#NotNull
#Column(name = "department", nullable = false)
#Enumerated(EnumType.STRING)
private Department department;
#ManyToMany
#JoinTable(
name = "approved_courses",
joinColumns = #JoinColumn(name = "uni_course_id"),
inverseJoinColumns = #JoinColumn(name = "host_course_id"))
private Set<HostCourse> approvedCourses;
public UniCourse(
#JsonProperty("id") UUID id,
#JsonProperty("courseCode") String courseCode,
#JsonProperty("courseName") String courseName,
#JsonProperty("ects") Double ects,
#JsonProperty("department") Department department) {
super(id, courseCode, courseName, ects);
this.department = department;
this.bilkentCredit = bilkentCredit;
}
}
and,
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Table(name = "host_course")
public class HostCourse extends Course {
#NotBlank
#Column(name = "syllabus", nullable = false)
private String syllabus;
#NotBlank
#Column(name = "web_page", nullable = true)
private String webPage;
#NotNull
#Column(name = "university_id", nullable = false)
private UUID universityId;
#ManyToMany(mappedBy = "approvedCourses")
private Set<UniCourse> uniCourses;
public HostCourse(
#JsonProperty("id") UUID id,
#JsonProperty("courseCode") String courseCode,
#JsonProperty("courseName") String courseName,
#JsonProperty("ects") Double ects,
#JsonProperty("syllabus") String syllabus,
#JsonProperty("webPage") String webPage,
#JsonProperty("universityId") UUID universityId) {
super(id, courseCode, courseName, ects);
this.syllabus = syllabus;
this.webPage = webPage;
this.universityId = universityId;
this.courseApproval = courseApproval;
}
}
I want to get approvedCourses using universityId of HostCourse. How can I achieve this?
I tried using the findByApprovedCoursesUniversityId(UUID universityId) method and it returned the following:
{
"data": [
{
"courseCode": "CS315",
"courseName": "Programming Languages",
"ects": 5.0,
"department": "CS",
"courseId": "8a890716-aaf1-40bb-81b2-e0a638b878dd",
"approvedCourses": []
}
]
"timestamp": "2022.12.15 21.48.32",
"status": "OK"
}
I also want to get approved courses but it returns an empty array.

Related

How to fetch data from "Many to many" relationship table? JPA +SPRINGBOOT

I am wondering how to fetch the data from many to many relationship table that was autogenerated with JPA?
Database design
I wrote some query code that gives me exactly the data I would like to fetch. Although I am not sure how to translate it to the JPA:
SELECT id, first_name, last_name, phone_number, shift_id FROM
FROM users
RIGHT JOIN employees_enrolled
ON users.id = employees_enrolled.employee_id
Employee extends User.
User model:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "username", nullable = false)
private String username;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
Employee model:
#Entity(name = "employee")
public class Employee extends User implements Serializable {
private static final long serialVersionUID = 6529685098267757690L;
#JsonIgnore
#ManyToMany(mappedBy = "enrolledEmployees")
private List<Shift> shifts = new ArrayList<>();
#Column(name = "address", nullable = false)
private String address;
#Column(name = "hours", nullable = false)
private double hours;
public Employee() {}
public Employee(long id)
{
super(id);
}
public Employee(long id, String username, String password, String firstName, String lastName,
String email, String phoneNumber, String authLevel, String address, double hours) {
super(id, username, password, firstName, lastName, email, phoneNumber, authLevel);
this.address = address;
this.hours = hours;
}
Shift model:
#Entity
#Table(name = "shift")
public class Shift implements Serializable {
private static final long serialVersionUID = -7065683873804696266L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "employees_enrolled",
joinColumns = {#JoinColumn(name = "shift_id", referencedColumnName = "id",
insertable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "employee_id",
referencedColumnName = "id", insertable = false, updatable = false)})
private List<Employee> enrolledEmployees = new ArrayList<>();
private Long employee_id;
#Column(name = "description", nullable = false)
private String description;
#Column(name = "address", nullable = false)
private String address;

How can I implement a JPQL query retrieving all the user having a specific type (implemented using a MANY TO MANY relation)?

I am working on a Spring Boot project and I am not so into Hibernate/JPA and I have the following doubt about how can I use it to perform the following query.
I have these 2 entity classes:
User: it contains records representing user on my portal:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
//private String test;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
}
}
Each user can be associated with one or more roles reppresented by this MANY TO MANY relationship:
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
As you can see this field is associated to a DB table named portal_user_user_type that simply contains the PK of the user table and the PK of the user_type table represented by the following entity class:
#Entity
#Table(name = "user_type")
#Getter
#Setter
public class UserType implements Serializable {
private static final long serialVersionUID = 6904959949570501298L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "type_name")
private String typeName;
#Column(name = "description")
private String description;
//#OneToMany(mappedBy = "userType")
//#JsonManagedReference
//private Set<User_UserType> userToUserTypeAssociation = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "user_type_operation",
joinColumns = { #JoinColumn(name = "fk_user_type_id") },
inverseJoinColumns = { #JoinColumn(name = "fk_operation_id") }
)
Set<Operation> operations;
public UserType() {
super();
// TODO Auto-generated constructor stub
}
public UserType(String typeName, String description) {
super();
this.typeName = typeName;
this.description = description;
}
}
Now the previous class is mapping the user_type database typological table that can contains only a specific set of values. For example having typeName like ADMIN or AGENT or CLIENT.
Into my Spring Boot project I have this repository interface implementing JpaRepository interface:
public interface UsersRepository extends JpaRepository<User, Integer> {
User findByemail(String email);
}
At the moment it contains onlya query implemented using the "query by method name" tecnique.
Now I have to implement another more complex query based on the previous entity classes:
I have to retrieve all the User instance having a specific UserType.typeName value.
For example: retrieve all the User record having UserType.typeName equals to ADMIN.
Is it possible implement it using the query by method name style or is it better to use JPQL. In case how can I create a query like this? (also in JPQL is ok)
you can use #Query annotation in order to write your query or you can use findByUserTypes_TypeNameIn(List typeNameList) for a list of user type names or findByUserTypes_TypeName(String typeName) for a specific type name.

adding new entity in database through PostMapping

I have two entities
#Entity
#Table(name = "categories")
public class Category {
#Getter
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id", unique = true, nullable = false)
private long categoryId;
#Getter #Setter
#ManyToMany(mappedBy = "categories", cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
List<Product> products;
#Getter #Setter
#Column(name = "category_name", nullable = false, unique = true)
private String categoryName;
And
#Entity
#Table(name = "products")
public class Product {
#Getter
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id", unique = true, nullable = false)
private long productId;
#Getter #Setter
#Column(name = "price")
private float price;
#Getter #Setter
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "categories_product")
private List<Category> categories;
#Getter #Setter
#Column(name = "product_code", unique = true, nullable = false)
private String productCode;
#Getter #Setter
#Column(name = "product_name", nullable = false)
private String productName;
#Getter #Setter
#Column(name = "description", nullable = false)
private String description;
#Getter #Setter
#Column(name = "short_description", nullable = false)
private String shortDescription;
}
I`m using MapStruct for DTO. When I want to add new product through controller I get the following error:
org.hibernate.PropertyValueException: not-null property references a null or transient value : com.project.shop.models.Category.categoryName
As I understand, hibernate tries to create a new Category, when I want it to use an already existing one in database.
CategoryDTO:
#Getter
#Setter
public class CategoryDto {
private long categoryId;
private String categoryName;
private boolean categoryActive;
}
ProductDTO:
#Getter
#Setter
public class ProductDto {
private String productName;
private String productCode;
private float price;
private String shortDescription;
private String description;
private List<CategoryDto> categories;
}
CategoryMapper:
#Mapper(componentModel = "spring")
public interface CategoryMapper {
CategoryDto toDto(Category category);
List<CategoryDto> toDtos(List<Category> categories);
List<Category> toModels(List<CategoryDto> categoryDtos);
Category toModel(CategoryDto categoryDto);
}
ProductMapper:
#Mapper(uses = {CategoryMapper.class},
componentModel = "spring")
public interface ProductMapper {
ProductDto toDto(Product product);
List<ProductDto> toDtos(List<Product> products);
List<Product> toModels(List<ProductDto> productDtos);
Product toModel(ProductDto productDto);
}
Controller:
#PostMapping("/product")
public ResponseEntity<ProductDto> create(#RequestBody ProductDto productDto) {
productService.save(productMapper.toModel(productDto));
return ResponseEntity.status(HttpStatus.CREATED).body(productDto);
}
productService.save:
public void save(Product product) {
productRepository.save(product);
}
It is not that easy basically. My suggestion (and my implementation) is that you pass only the categoryId with your ProductDTO. And at the service, take this ID, find the respective Category via a Repository and then set the Product's Category to this entity.
Simple example:
public ProductDTO addProduct(ProductDTO newDto) {
Category category = categoryRepo.findById(newDto.getCategory().getId())
.orElseThrow(// something);
Product entity = modelMapper.map(newDto, Product.class); // This does the same thing as your mapper, You can also implement this in your project
entity.setCategory(category );
return modelMapper.map(productRepo.save(entity), ProductDTO.class); // This saves the entity and converts it to a DTO and returns it
}
If you look at the identity, nullable = false option appears to be defined option is defined.
#Entity
#Table(name = "categories")
public class Category {
....
#Getter #Setter
#Column(name = "category_name", nullable = false, unique = true)
private String categoryName;
I think it would be good to look for the categoryName column value of CategoryDto first.

How to convert a SQL query to Spring JPA query

I have a SQL query like this:
"Select UIProfileID from UserTable where UPPER(UserID) = UPPER('?1')".
I want to convert it to Spring JPA.
I want to write getUIProfileId() and return Integer. But I don't know how to implement. Because User table doesn't have UIProfileId column that it was joined from UIProfileTable table. Please help me solve it.
Currently, I have tables:
User.java
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(name = "UserTable")
public class User {
#Column(name = "UserID", length = 32, nullable = false)
#Id
private String name;
#ManyToOne
#JoinColumn(name = "DomainID", nullable = false)
private Domain domain;
#Column(name = "Password", length = 32, nullable = false)
private String password;
#ManyToOne
#JoinColumn(name = "UIProfileID", nullable = false)
private UIProfile uiProfile;
#Column(name = "ResPerpage", nullable = false)
private Integer resperpage;
#Column(name = "DefaultTab")
private Integer defaulttab;
#ManyToOne
#JoinColumn(name = "AdminProfile")
private AdminProfiles adminProfile;
#Column(name = "LanguageId")
private Integer languageId;
}
UIProfile.java
#Entity
#Getter
#Setter
#Table(name = "UIProfileTable")
public class UIProfile implements Serializable {
#Id
#Column(name = "UIProfileID", length = 11, nullable = false)
private Integer id;
#Column(name = "UIProfileName", length = 32, nullable = false)
private String name;
#OneToMany(mappedBy = "id.uiProfile")
private List<UIProfileTopLevel> topLevels;
}
UserRepository.java
public interface UserRepository extends Repository<User, String> {
Optional<User> findOne(String name);
#Query("Select UIProfileID from User where UPPER(UserID) = UPPER('admin')")
Integer getUIProfileId();
}
You can try this:
#Query("SELECT u.uiProfile.id from User u where UPPER(u.name)=UPPER('admin')")
Integer getUIProfileId();
Here User is the domain class name and u is the reference of User. with u we will access User's field NOT the column name which are specified with #Column or #JoinColumn Ex : #JoinColumn(name = "UIProfileID", nullable = false).

Hibernate one to many mapping annotation issue

I am new to Hibernate. I have two tables Team(parent) and Product(child) with TEAM_ID column as a relationship, each team will have multiple products and each product will have single team. I have created entity classes with #OneToMany mapping in Team class and #ManyToOne in Product class.
I need to coverup below scenarios,
To save both product and team when team is new
Save only product if the team is already available
When i am trying to save product it tries to save team again throws constraint error.
Please help.
Team:
#Entity
#Table(name = "TEAM")
public class Team implements Serializable{
private static final long serialVersionUID = 5819170381583611288L;
#Id
#SequenceGenerator(name="teamIdSeq",sequenceName="team_id_seq",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="teamIdSeq")
#Column(name="TEAM_ID", updatable = false, nullable = false, unique = true)
private int teamId;
#Column(name="NAME", nullable = false, unique = true)
private String teamName;
#Column(name="DESCRIPTION", nullable = false)
private String teamDesc;
#Column(name="CONTACTS", nullable = false)
private String contacts;
#Column(name="APPROVER_NAME", nullable = false)
private String approverName;
#Column(name="APPROVAL_STATUS", nullable = false)
private int approvalStatus;
#Temporal(TemporalType.DATE)
#Column(name="CREATED_ON", nullable = false)
private Date createdOn;
#Column(name="CREATED_BY", nullable = false)
private String createdBy;
#Temporal(TemporalType.DATE)
#Column(name="MODIFIED_ON", nullable = false)
private Date modifiedOn;
#Column(name="MODIFIED_BY", nullable = false)
private String modifiedBy;
#OneToMany(fetch = FetchType.LAZY, mappedBy="team", cascade = CascadeType.ALL)
private Set<Product> products;
//setters and getters
}
Product:
#Entity
#Table(name = "PRODUCT", uniqueConstraints = {#UniqueConstraint(columnNames = {"PRODUCT_ID", "TEAM_ID"})})
public class Product implements Serializable{
private static final long serialVersionUID = 5819170381583611288L;
#Id
#SequenceGenerator(name="productIdSeq", sequenceName="product_id_seq",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="productIdSeq")
#Column(name="PRODUCT_ID", updatable = false, nullable = false)
private int productId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TEAM_ID")
private Team team;
#Column(name="NAME", nullable = false, unique = true)
private String productName;
#Column(name="DESCRIPTION", nullable = true)
private String productDesc;
#Column(name="APPROVER_NAME", nullable = false)
private String approverName;
#Column(name="APPROVAL_STATUS", nullable = false)
private int approvalStatus;
#Temporal(TemporalType.DATE)
#Column(name="CREATED_ON", nullable = false)
private Date createdOn;
#Column(name="CREATED_BY", nullable = false)
private String createdBy;
#Temporal(TemporalType.DATE)
#Column(name="MODIFIED_ON", nullable = false)
private Date modifiedOn;
#Column(name="MODIFIED_BY", nullable = false)
private String modifiedBy;
#OneToMany(fetch = FetchType.LAZY, mappedBy="product")
private Set<Form> forms;
//setters and getters
}
DAO:
#Repository
#EnableTransactionManagement
public class KMDBDAOImpl implements KMDBDAO {
#Autowired
private SessionFactory sessionFactory;
public void addTeam(Team team) {
Product product = new Product(team, "BMA" + Math.random(), "UI Tool", "test",
1, new Date(), "test", new Date(), "test");
Set<Product> products = new HashSet<Product>();
products.add(product);
team.setProducts(products);
if(getTeam(team.getTeamName()) != null) {
product.setTeam(getTeam(team.getTeamName()));
sessionFactory.getCurrentSession().saveOrUpdate(product);
} else {
sessionFactory.getCurrentSession().saveOrUpdate(team);
}
}
public Team getTeam(String teamName) {
Query query = sessionFactory.getCurrentSession().createQuery("from Team where teamName = :name");
query.setString("name", "teamName");
return (query.list().size() > 0 ? (Team) query.list().get(0) : null);
}
The only time that you should set Product list on Team is when Team is a new entity. So:
Set<Product> products = new HashSet<Product>();
products.add(product);
if(getTeam(team.getTeamName()) != null) {
product.setTeam(getTeam(team.getTeamName()));
sessionFactory.getCurrentSession().saveOrUpdate(product);
} else {
team.setProducts(products);
sessionFactory.getCurrentSession().saveOrUpdate(team);
}
i give you some example code for one to many relationship please go through it and let me kn if some problem .... i have 2 tables 1.product 2.sku my condition is , one product have many sku's ...
Product.java
#LazyCollection(LazyCollectionOption.FALSE)
#ElementCollection(targetClass=Product.class)
#OneToMany(mappedBy="product" , cascade=CascadeType.MERGE)
private List<Sku> listSkuOrders = new ArrayList<>();
Sku.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = PRODUCT_ID , nullable = false)
private Product product;

Categories

Resources