ManyToOne, oneToMany with join table - mysql

I have this example, witch it works, I just want to modify it but I can't do it.
Here is the link of the example :example link
Anyway I posting it here to:
Here is the picture of entitys :
Entity-Relation
CREATE TABLE `stock` (
`STOCK_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`STOCK_CODE` VARCHAR(10) NOT NULL,
`STOCK_NAME` VARCHAR(20) NOT NULL,
PRIMARY KEY (`STOCK_ID`) USING BTREE,
UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
)
CREATE TABLE `category` (
`CATEGORY_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`NAME` VARCHAR(10) NOT NULL,
`DESC` VARCHAR(255) NOT NULL,
PRIMARY KEY (`CATEGORY_ID`) USING BTREE
)
CREATE TABLE `stock_category` (
`STOCK_ID` INT(10) UNSIGNED NOT NULL,
`CATEGORY_ID` INT(10) UNSIGNED NOT NULL,
`CREATED_DATE` DATE NOT NULL,
`CREATED_BY` VARCHAR(10) NOT NULL,
PRIMARY KEY (`STOCK_ID`,`CATEGORY_ID`),
CONSTRAINT `FK_CATEGORY_ID` FOREIGN KEY (`CATEGORY_ID`)
REFERENCES `category` (`CATEGORY_ID`),
CONSTRAINT `FK_STOCK_ID` FOREIGN KEY (`STOCK_ID`)
REFERENCES `stock` (`STOCK_ID`)
)
And here are the java classes:
Stock.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
#Entity
#Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
#UniqueConstraint(columnNames = "STOCK_NAME"),
#UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
private Integer stockId;
private String stockCode;
private String stockName;
private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);
public Stock() {
}
public Stock(String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Stock(String stockCode, String stockName,
Set<StockCategory> stockCategories) {
this.stockCode = stockCode;
this.stockName = stockName;
this.stockCategories = stockCategories;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
#Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
#Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.stock", cascade=CascadeType.ALL)
public Set<StockCategory> getStockCategories() {
return this.stockCategories;
}
public void setStockCategories(Set<StockCategory> stockCategories) {
this.stockCategories = stockCategories;
}
}
StockCategory.java
package com.mkyong.stock;
import java.util.Date;
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
#Entity
#Table(name = "stock_category", catalog = "mkyongdb")
#AssociationOverrides({
#AssociationOverride(name = "pk.stock",
joinColumns = #JoinColumn(name = "STOCK_ID")),
#AssociationOverride(name = "pk.category",
joinColumns = #JoinColumn(name = "CATEGORY_ID")) })
public class StockCategory implements java.io.Serializable {
private StockCategoryId pk = new StockCategoryId();
private Date createdDate;
private String createdBy;
public StockCategory() {
}
#EmbeddedId
public StockCategoryId getPk() {
return pk;
}
public void setPk(StockCategoryId pk) {
this.pk = pk;
}
#Transient
public Stock getStock() {
return getPk().getStock();
}
public void setStock(Stock stock) {
getPk().setStock(stock);
}
#Transient
public Category getCategory() {
return getPk().getCategory();
}
public void setCategory(Category category) {
getPk().setCategory(category);
}
#Temporal(TemporalType.DATE)
#Column(name = "CREATED_DATE", nullable = false, length = 10)
public Date getCreatedDate() {
return this.createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
#Column(name = "CREATED_BY", nullable = false, length = 10)
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
StockCategory that = (StockCategory) o;
if (getPk() != null ? !getPk().equals(that.getPk())
: that.getPk() != null)
return false;
return true;
}
public int hashCode() {
return (getPk() != null ? getPk().hashCode() : 0);
}
}
Stockcategoryid.java
package com.mkyong.stock;
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;
#Embeddable
public class StockCategoryId implements java.io.Serializable {
private Stock stock;
private Category category;
#ManyToOne
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
#ManyToOne
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StockCategoryId that = (StockCategoryId) o;
if (stock != null ? !stock.equals(that.stock) : that.stock != null) return false;
if (category != null ? !category.equals(that.category) : that.category != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (stock != null ? stock.hashCode() : 0);
result = 31 * result + (category != null ? category.hashCode() : 0);
return result;
}
}
Category.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {
private Integer categoryId;
private String name;
private String desc;
private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);
public Category() {
}
public Category(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Category(String name, String desc, Set<StockCategory> stockCategories) {
this.name = name;
this.desc = desc;
this.stockCategories = stockCategories;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CATEGORY_ID", unique = true, nullable = false)
public Integer getCategoryId() {
return this.categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
#Column(name = "NAME", nullable = false, length = 10)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "[DESC]", nullable = false)
public String getDesc() {
return this.desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.category")
public Set<StockCategory> getStockCategories() {
return this.stockCategories;
}
public void setStockCategories(Set<StockCategory> stockCategories) {
this.stockCategories = stockCategories;
}
}
Now I want for a stock to have only one Category, so basicaly I have to change from #OneToMany In Stock.java to ManyToOne but if I only changed to that it gives me error.
Can someone help me, please .
It should be something like :
#ManyToOne(fetch = FetchType.LAZY, mappedBy = "pk.stock", cascade=CascadeType.ALL)
private StockCategory stockgatery;
//setter and getter methods
//or something like
#ManyToOne
#JoinColumn(name="pk.stock"
But I can't figure it out.

Try something like this below ...
#ManyToOne #JoinColumns({ #JoinColumn(name = "STOCK_ID", referencedColumnName = "STOCK_ID"),
#JoinColumn(name = "CATEGORY_ID", referencedColumnName = "CATEGORY_ID") })
private StockCategory stockCategory;

Related

joining multiple entities in Spring JPA

Have Users, Roles and User Roles tables.
Users
id
username
Roles
role_id
role_name
user_roles
user_id (fk users table)
role_id (fk roles table)
I defined my entities this way
user_roles Entity
#Column(name = "USER_ID")
private int userId;
#Column(name = "ROLE_ID")
private int roleId;
#ManyToOne()
#JoinColumn(name="ROLE_ID", insertable = false, updatable = false)
private RoleGroup userRole;
#ManyToOne()
#JoinColumn(name="id", insertable = false, updatable = false)
private User user;
User entity:
#Id
#Column(name="ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "USER_NAME")
private String userName;
Roles Entity:
#Id
#Column(name="ROLE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "ROLE_NAME")
private String roleName;
#OneToMany(targetEntity=UserRoleGroup.class, mappedBy="userRole",cascade=CascadeType.ALL,
fetch = FetchType.LAZY)
private List<UserRoleGroup> userGroup;
User table will store all lists of users. Roles table is the master table which is having 5 rows - (Admin, Read, manager..)
User Role Table will have user_id from user table and role_id from roles table.
Example
USER:
id user_name
1 test#gmail.com
2 abc#gmail.com
Roles
role_id Name
1 Admin
2 Manager
3 Read
User Roles
User_Id Role_ID
1 1
1 2
2 3
Repository
#Query("FROM UserRoleGroup AS urg LEFT JOIN urg.userRole AS ur LEFT JOIN urg.user AS u WHERE u.userName = ?1")
public List<UserRoleGroup> findAllUserRoles(String userName);
No compilation error, no runtime error, but query isn't returning anything whereas I have data in table. when i run query in MySql I am getting data
I was able to get this working with some minor changes to the entity classes and by adding a DTO. I think its not a good practice to pass around the entity to the client. Below is my implementation.
UserRoleGroup
#Entity
#Table#Data
public class UserRoleGroup {
#EmbeddedId
private UserRoleId id;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("userId")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("roleId")
private Role role;
}
UserRoleId
#Embeddable
public static class UserRoleId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "user_id")
private Integer userId;
#Column(name = "role_id")
private Integer roleId;
public UserRoleId() {
}
public UserRoleId(Integer userId, Integer roleId) {
super();
this.userId = userId;
this.roleId = roleId;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserRoleId that = (UserRoleId) o;
if (!userId.equals(that.userId)) return false;
return roleId.equals(that.roleId);
}
#Override
public int hashCode() {
int result = userId.hashCode();
result = 31 * result + roleId.hashCode();
return result;
}
}
User
import javax.persistence.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* user can have roles
* */
#Entity
#Table
public class User {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "username")
private String userName;
#OneToMany(mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true)
private Set<UserRoleGroup> roles = new HashSet<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Set<UserRoleGroup> getRoles() {
return roles;
}
public void setRoles(Set<UserRoleGroup> roles) {
this.roles = roles;
}
}
Role
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "roles")
public class Role {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "role_name")
private String roleName;
#OneToMany(mappedBy="role",
cascade=CascadeType.ALL,
fetch = FetchType.LAZY)
private List<UserRoleGroup> users = new ArrayList<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<UserRoleGroup> getUsers() {
return users;
}
public void setUsers(List<UserRoleGroup> users) {
this.users = users;
}
}
UserGroupRepository
import com.chait.json.generate.entity.UserRoleGroup;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface UserGroupRepository extends JpaRepository<UserRoleGroup,Integer> {
#Query(
"FROM UserRoleGroup AS urg LEFT JOIN urg.role AS ur LEFT JOIN urg.user AS u WHERE u.userName = ?1"
)
List<UserRoleGroup> findAllUserRoles(#Param("userName") String userName);
}
UserRoleDTO
#Data
#AllArgsConstructor
public class UserRoleDTO {
private String username;
private Set<String> roles;
}
UserRestController
#RestController
#RequestMapping("/users")
#RequiredArgsConstructor
public class UserRestController {
private final UserGroupRepository userGroupRepository;
#GetMapping("/{username}")
public ResponseEntity<UserRoleDTO> getUserRoleByUsername(
#PathVariable("username") String username
) {
List<UserRoleGroup> userRoleGroups = userGroupRepository.findAllUserRoles(username);
return ResponseEntity.ok(
new UserRoleDTO(
userRoleGroups.get(0).getUser().getUserName(),
userRoleGroups
.stream()
.map(UserRoleGroup::getRole)
.map(Role::getRoleName)
.collect(Collectors.toSet())
)
);
}
}

Trouble executing an JPQL query

I'm working with spring boot entity manger using JPQL with queries.
The following query is working correctly:
SELECT h, uh FROM Hour h, UserHour uh
But now I want to get a single result for which I use the query:
SELECT h FROM Hour h INNER JOIN UserHour uh WHERE uh.userId = 1
But then the results are zero, which is not expected.
But, when I use the SQL query:
SELECT * FROM hour
INNER JOIN user_hour
ON user_hour.hour_id = hour.hour_id
WHERE user_hour.user_id = '1'
I get the result I want.
Please help me out.
EDIT:
List<Object> result = entityManager.createQuery(
"select distinct hr " +
"from Hour hr " +
"join hr.users hu " +
"where hu.id = :userID")
.setParameter( "userID", 1)
.getResultList();
System.out.println(result);
User Entity:
package com.timely.backend.models;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
#Entity
#Table(name = "USER")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String fullname;
private String email;
private String creation_date;
// user roles
#ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
private Set<Role> roles;
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "user_hour",
joinColumns = #JoinColumn(name = "userId"),
inverseJoinColumns = #JoinColumn(name = "hourId")
)
private List<Hour> hours = new ArrayList<>();
public User() {
}
public User(String username, String password, String fullname, String email, String creation_date) {
this.username = username;
this.password = password;
this.fullname = fullname;
this.email = email;
this.roles = getRoles();
this.creation_date = creation_date;
}
//If user is admin
public boolean isAdmin() {
return roles.contains(Role.ADMIN);
}
//region Getters & Setters
public void setId(int id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setFullname(String fullName) {
this.fullname = fullName;
}
public void setEmail(String email) { this.email = email; }
public void setRoles(Set<Role> roles) { this.roles = roles; }
public void setCreation_date(String creation_date) { this.creation_date = creation_date; }
public int getId() { return id; }
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getFullname() {
return fullname;
}
public String getEmail() { return email; }
public Set<Role> getRoles() { return roles; }
public String getCreation_date() { return creation_date; }
//endregion
}
UserHour Entity:
package com.timely.backend.models;
import javax.persistence.*;
#Entity
#Table(name = "user_hour")
public class UserHour {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getHourId() {
return hourId;
}
public void setHourId(int hourId) {
this.hourId = hourId;
}
public UserHour(int userId, int hourId) {
this.userId = userId;
this.hourId = hourId;
}
public UserHour(){
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int userId;
private int hourId;
}
Hour Entity:
package com.timely.backend.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "HOUR")
public class Hour {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int hourId;
private String date;
private String startTime;
private String endTime;
public Hour(int hourId, String date, String startTime, String endTime) {
this.hourId = hourId;
this.date = date;
this.startTime = startTime;
this.endTime = endTime;
}
public Hour(){
}
#ManyToMany(mappedBy = "hours")
private List<User> users = new ArrayList<>();
public int gethourId() {
return hourId;
}
public void sethourId(int hourId) {
this.hourId = hourId;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
}
MySQL query working:
SELECT id, date, start_time, end_time FROM hour
JOIN user_hour
ON user_hour.hour_id = hour.hour_id
WHERE user_hour.user_id = '1'
Problem:
User Table:
UserHour Table:
Hour Table:

Many to Many relationship - get double rows in MySQL

Well just like the title says, I have a many to many relationship in my project, when customer can have many coupons and the other way. To make that work I made another table in MySQL which include coupon id and customer id(each row)but somehow every time I add a coupon to a customer It double its rows in coupon_customer table.
for example:
coupon-> id 1
customer->id 4
first add
now I add another coupon(id 2) to the same customer and that's the result:
second add
my code:
Customer:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "password")
private String password;
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH,
CascadeType.REFRESH })
#JoinTable(name = "coupon_customer", joinColumns = #JoinColumn(name = "customer_id"), inverseJoinColumns = #JoinColumn(name = "coupon_id"))
private List<Coupon> coupons;
public Customer() {
}
public Customer(String name, String password) {
this.name = name;
this.password = password;
this.coupons = new ArrayList<Coupon>();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#JsonIgnore
public List<Coupon> getCoupons() {
return coupons;
}
public void setCoupons(ArrayList<Coupon> coupons) {
this.coupons = coupons;
}
#Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", password=" + password + "]";
}
}
Coupon:
#Entity
#Table(name = "coupon")
public class Coupon {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "title")
private String title;
#Column(name = "start_date")
private Date startDate;
#Column(name = "end_date")
private Date endDate;
#Column(name = "amount")
private int amount;
#Enumerated(EnumType.STRING)
#Column(name = "type")
private CouponType type;
#Column(name = "message")
private String message;
#Column(name = "price")
private double price;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company company;
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH,
CascadeType.REFRESH })
#JoinTable(name = "coupon_customer", joinColumns = #JoinColumn(name = "coupon_id"), inverseJoinColumns = #JoinColumn(name = "customer_id"))
private List<Customer> customers;
public Coupon() {
}
public Coupon(String title, Date startDate, Date endDate, int amount, CouponType type, String message,
double price) {
this.title = title;
this.startDate = startDate;
this.endDate = endDate;
this.amount = amount;
this.type = type;
this.message = message;
this.price = price;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public CouponType getType() {
return type;
}
public void setType(CouponType type) {
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
#JsonIgnore
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
#JsonIgnore
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
#Override
public String toString() {
return "Coupon [id=" + id + ", title=" + title + ", startDate=" + startDate + ", endDate=" + endDate
+ ", amount=" + amount + ", type=" + type + ", message=" + message + ", price=" + price + "]";
}
CustomerController:
#RequestMapping(value = "/purchaseCoupon")
public ResponseEntity<CouponSystemResponse> purchaseCoupon(#RequestParam(value = "id") int id) {
try {
Coupon coupon = couponService.getCoupon(id);
getEntity().getCoupons().add(coupon); --> getEntity() gets the customer
coupon.setAmount(coupon.getAmount() - 1);
customerService.updateCustomer(getEntity()); --> updates customer after purchase coupon
couponService.updateCoupon(coupon); --> update coupon after been purchased(amount -1)
.....
and if that helps MySQL script:
DROP SCHEMA IF EXISTS `couponsystem`;
CREATE SCHEMA `couponsystem`;
use `couponsystem`;
DROP TABLE IF EXISTS `company`;
CREATE TABLE `company` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL UNIQUE,
`password` varchar(20) NOT NULL,
`email` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
);
DROP TABLE IF EXISTS `coupon`;
CREATE TABLE `coupon` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(20) NOT NULL UNIQUE,
`start_date` datetime DEFAULT NULL,
`end_date` datetime DEFAULT NULL,
`amount` int DEFAULT NULL,
`type` varchar(15) DEFAULT NULL,
`message` varchar(50) DEFAULT NULL,
`price` float DEFAULT NULL,
`company_id` int(11),
PRIMARY KEY (`id`),
KEY `FK_company_id` (`company_id`),
CONSTRAINT `FK_company_id` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL UNIQUE,
`password` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `coupon_customer`(
`coupon_id` int(11) NOT NULL,
`customer_id` int(11) NOT NULL,
/*
PRIMARY KEY (`coupon_id`,`customer_id`), --> that's in comment only cause I got exception every time row doubles itself and tried looking for solutions
*/
CONSTRAINT `FK_coupon_id` FOREIGN KEY (`coupon_id`) REFERENCES `coupon` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);
CustomerService:
#Service
public class CustomerService {
#Autowired
CustomerRepository customerRepo;
.....
public void updateCustomer(Customer customer) {
customerRepo.save(customer);
}
.....
CouponService:
#Service
public class CouponService {
#Autowired
CouponRepository couponRepo;
......
public void updateCoupon(Coupon coupon) {
couponRepo.save(coupon);
}
......
Weird stuff.Like it takes all the last rows add them and then add another rows. I thought i something with cascade but couldn't make that work.... appreciate any help.
1st of all I would add another constraint to the coupon_customer table, a unique combination, provided with an INSERT IGNORE conmand, that will skip insert errors, it will provide basic db protection of such errors
ALTER TABLE coupon_customer ADD UNIQUE KEY coupon_customer (coupon_id, customer_id);
and the INSERT should be:
INSERT IGNORE INTO...
Beyond that, the function generating the query should receive exactly one parameter for each key and generate the simplest query. If the insert js built with select or the function works on the function with arrays, then these can generate errors as you described
public function add coupon($customer_id, $coupon_id) {
...
$sql = "INSERT IGNORE INTO coupon_customer VALUES (". $customer_id . ",". $coupon_id . ");" ;
...
}
Your Coupon_Customer primary key should be composed by two fields (customer_id and coupon_id).
Looking at your code, you don't have any primary key at this table. That's the main problem.
In order to create a composed primary key in Spring Data JPA, you do need a #Embeddable annotated class, which will represent your coupon_customer_id.
Something like the following:
CouponCustomerId.java
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
#Embeddable
public class CouponCustomerId implements Serializable {
#Column(name = "coupon_id")
private Long couponId;
#Column(name = "customer_id")
private Long customerId;
public CouponCustomerId(Long couponId, Long customerId) {
this.couponId = couponId;
this.customerId = customerId;
}
// getters and setters..
}
Now, you'll need to create a CouponCustomer entity with an #EmbeddedId, which will represent your composed primary key.
CouponCustomer.java
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.OneToMany;
#Entity
public class CouponCustomer {
#EmbeddedId
private CouponCustomerId id;
#ManyToOne(fetch = FetchType.LAZY) // add your own ManyToOne configurations
#MapsId("couponId")
private Coupon coupon;
#ManyToOne(fetch = FetchType.LAZY) // add your own ManyToOne configurations
#MapsId("customerId")
private Customer customer;
// getters and setters..
}
Now, at your Customer entity, you'll have to change your List<Coupon> to List<CouponCustomer> and change the relationship to #OneToMany.
Customer.java
....
#OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH,
CascadeType.REFRESH
})
private List<CouponCustomer> coupons;
....
Same thing for the Coupon entity.
Coupon.java
....
#OneToMany(mappedBy = "coupon" fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH,
CascadeType.REFRESH
})
private List<CouponCustomer> customers;
....
Now, each time you add a coupon to a customer, you just need to relate it's ids.
Something like the following:
#RequestMapping(value = "/purchaseCoupon")
public ResponseEntity < CouponSystemResponse > purchaseCoupon(#RequestParam(value = "id") int id) {
try {
Coupon coupon = couponService.getCoupon(id);
coupon.setAmount(coupon.getAmount() - 1);
couponService.updateCoupon(coupon); -->update coupon after been purchased(amount - 1)
CouponCustomer cc = new CouponCustomer();
// assuming that getEntity() gets your Customer
cc.setCoupon(coupon);
cc.setCustomer(getEntity();
cc.setId(new CouponCustomerId(coupon.getId(), getEntity().getId()));
couponCustomerService.save(cc);
.....
Keep in mind that, in order to update the Coupon and creating a record in Coupon_customer, you don't need to call customerService.updateCustomer.
At
cc.setId(new CouponCustomerId(coupon.getId(), getEntity().getId()));
couponCustomerService.save(cc);
You're creating a record to the coupon_customer table, with the composed primary key (coupon_id, customer_id).
Hope this helps.

Mapping a map to table using eclipselink causes eclipselink to concatenate entity name and entityId in the sql query

My aim is to learn JavaEE and what better way to do so than to work on a project. So I set out to create a Stock Market simulation web application.
Naturally a person owns some stock identified by a company ticker (company_id) and an associated number of shares owned. So I put these in a Map.
Here are the mysql ddl statements;
For the users table
CREATE TABLE `users` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT,
`firstName` varchar(30) NOT NULL,
`lastName` varchar(30) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=latin1;
For the portfolios table,
CREATE TABLE `portfolios_tb` (
`user_id` bigint(20) NOT NULL,
`company_id` varchar(4) NOT NULL,
`shares_owned` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`,`company_id`),
KEY `company_id` (`company_id`),
CONSTRAINT `company_id` FOREIGN KEY (`company_id`) REFERENCES `stocks` (`company_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `uid` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The Stock entity
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
/**
*
*/
#Entity
#Table(name = "stocks")
public class Stock implements Serializable {
#Id
#GeneratedValue
#Column(name = "company_id")
String stockId;
#NotNull #Column(name="company_name")
String companyName;
#NotNull #Column(name="shares_listed")
BigInteger sharesListed;
#Column(name="par_value")
BigDecimal parValue;
#Column(name="current_Price")
BigDecimal currentPrice;
public Stock(){
}
public Stock(String stockId, String companyName, BigInteger sharesListed){
this.companyName = companyName;
this.stockId = stockId;
this.sharesListed = sharesListed;
this.parValue = BigDecimal.ZERO;
this.currentPrice = BigDecimal.ZERO;
}
public String getStockId() {
return stockId;
}
public void setStockId(String stockId) {
this.stockId = stockId;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public BigInteger getSharesListed() {
return sharesListed;
}
public void setSharesListed(BigInteger sharesListed) {
this.sharesListed = sharesListed;
}
public BigDecimal getParValue() {
return parValue;
}
public void setParValue(BigDecimal parValue) {
this.parValue = parValue;
}
public BigDecimal getCurrentPrice() {
return currentPrice;
}
public void setCurrentPrice(BigDecimal currentPrice) {
this.currentPrice = currentPrice;
}
}
The User entity
import java.io.Serializable;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue
private String user_id; //I know its not convention, was experimenting.
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#ElementCollection
#CollectionTable(name = "portfolios_tb")
#MapKeyColumn(name = "company_id")
#Column(name = "shares_owned")
Map<String, BigInteger> stocksOwned = new HashMap<>();
public User() {
}
public User(String firstName, String lastName) {
this.stocksOwned = new HashMap<>();
this.firstName = firstName;
this.lastName = lastName;
}
public String getUser_id() {
return user_id;
}
public void setUser_id(String user_id) {
this.user_id = user_id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Map<String, BigInteger> getStocksOwned() {
return stocksOwned;
}
public void setStocksOwned(Map<String, BigInteger> stocksOwned) {
this.stocksOwned = stocksOwned;
}
}
Here's the main class
public class Main {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hisaMarket_Version2PU");
EntityManager em = emf.createEntityManager();
public static void main(String[] args) {
Main main = new Main();
main.getUsers();
}
public void getUsers(){
EntityTransaction tx = em.getTransaction();
tx.begin();
TypedQuery<User> query = em.createQuery("SELECT u FROM User u", User.class);
List<User> users = query.getResultList();
for(User user : users){
System.out.print(user.getFirstName() + " "+ user.getLastName() +" owns ");
Map<String,BigInteger> stocks = user.getStocksOwned();
Set<String> keys = stocks.keySet();
//planning to display Map key and corresponding value
System.out.println();
}
tx.commit();
em.close();
emf.close();
}
When I run it I get this message from eclipselink
Error Code: 1054
Call: SELECT t0.shares_owned, t0.company_id FROM portfolios_tb t0 WHERE (t0.User_USER_ID = ?)
bind => [1 parameter bound]
Query: DataReadQuery(name="stocksOwned" sql="SELECT t0.shares_owned, t0.company_id FROM portfolios_tb t0 WHERE (t0.User_USER_ID = ?)")
Why is eclipselink concatenating the entityname (User) and the enitityId (user_id) to give this t0.User_USER_ID = ? instead of this "....to.user_id"
That's because you didn't specify #JoinColumn for that mapping, so JPA's default mechanism is generating the join column name like <entity_name>_<id_column_name>.
Just add #JoinColumn(name = "user_id") attribute on #CollectionTable map mapping and it should work.
#ElementCollection
#CollectionTable(name = "portfolios_tb", joinColumns = #JoinColumn(name = "user_id"))
#MapKeyColumn(name = "company_id")
#Column(name = "shares_owned")
Map<String, BigInteger> stocksOwned = new HashMap<>();

#EmbeddedId throws IdIdentifierGenerationException: null id generated even with #GeneratedValue(strategy = GenerationType.AUTO)

Hi my relation throws #EmbeddedId throws IdIdentifierGenerationException: null id. Any advices is welcome. Here is my Code:
TABLE ACTIVIDADES(
CODIGO CHAR(10) NOT NULL UNIQUE,
NOMBRE VARCHAR(50) UNIQUE,
PRIMARY KEY(CODIGO)
)ENGINE=INNODB;
CREATE TABLE EVENTOS_ACTIVIDADES(
ID INT AUTO_INCREMENT,
CODIGO_ACTIVIDADES CHAR(10) NOT NULL UNIQUE,
PRIMARY KEY(ID,CODIGO_ACTIVIDADES),
FOREIGN KEY(CODIGO_ACTIVIDADES) REFERENCES ACTIVIDADES(CODIGO) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=INNODB;
Here are my JPA Entities:
#Entity
#Table(name = "eventos_actividades", catalog = "capacitacion_csg", uniqueConstraints = #UniqueConstraint(columnNames = "CODIGO_ACTIVIDADES"))
public class EventosActividades implements java.io.Serializable {
private EventosActividadesId id;
private Actividades actividades;
public EventosActividades() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = "ID", nullable = false)),
#AttributeOverride(name = "codigoActividades", column = #Column(name = "CODIGO_ACTIVIDADES", unique = true, nullable = false, length = 10)) })
public EventosActividadesId getId() {
return this.id;
}
public void setId(EventosActividadesId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CODIGO_ACTIVIDADES", unique = true, nullable = false, insertable = false, updatable = false)
public Actividades getActividades() {
return this.actividades;
}
public void setActividades(Actividades actividades) {
this.actividades = actividades;
}
#Entity
#Table(name="ACTIVIDADES", catalog="CAPACITACION_CSG", uniqueConstraints = {#UniqueConstraint(columnNames="NOMBRE"), #UniqueConstraint(columnNames="CODIGO")})
public class Actividades {
private String codigo;
private String nombre;
private List<EventosActividades> eventosActividades;
#Column(name="NOMBRE",unique=true,nullable=false,length=50)
public String getNombre() {
return nombre;
}
#Id
#Column(name="CODIGO",unique=true,nullable=false,length=10)
public String getCodigo() {
return codigo;
}
public void setCodigo(String codigo) {
this.codigo = codigo;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
#OneToMany(fetch= FetchType.EAGER,mappedBy="actividades",cascade=CascadeType.ALL)
public List<EventosActividades> getEventosActividades() {
return eventosActividades;
}
public void setEventosActividades(List<EventosActividades> eventosActividades) {
this.eventosActividades = eventosActividades;
}
}
#Embeddable
public class EventosActividadesId implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String codigoActividades;
public EventosActividadesId() {
}
public EventosActividadesId(int id, String codigoActividades) {
this.id = id;
this.codigoActividades = codigoActividades;
}
#Column(name = "ID", nullable = false)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "CODIGO_ACTIVIDADES", unique = true, nullable = false, length = 10)
public String getCodigoActividades() {
return this.codigoActividades;
}
public void setCodigoActividades(String codigoActividades) {
this.codigoActividades = codigoActividades;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof EventosActividadesId))
return false;
EventosActividadesId castOther = (EventosActividadesId) other;
return (this.getId() == castOther.getId())
&& ((this.getCodigoActividades() == castOther
.getCodigoActividades()) || (this
.getCodigoActividades() != null
&& castOther.getCodigoActividades() != null && this
.getCodigoActividades().equals(
castOther.getCodigoActividades())));
}
public int hashCode() {
int result = 17;
result = 37 * result + this.getId();
result = 37
* result
+ (getCodigoActividades() == null ? 0 : this
.getCodigoActividades().hashCode());
return result;
}
}
THANKS for everyone, I've not initialize my #Embeddable EventosActividadesId class and set in my EventosActividades Entity like this way:
EventosActividadesId id = new EventosActividadesId();
id.setCodigoActividades("ACTCBK");
eventosActividades.setId(id);