I have two tables: users and userdetails as follows:
package com.example.easynotes.model;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Table(name = "users")
#IdClass(UserID.class)
public class User implements Serializable {
#Id
int id;
#Id
String name;
String department;
//getters and setters
}
The userdetails classes will be this:
public class UserDetails implements Serializable{
int id;
String name;
String address;
String otherFields;
//getters and setters
}
id and name in users is a composite primary and I want the same fields in userdetails to be the foreign key. How can I achieve this in hibernate ?
We need to put both key in #Embeddable to detach compound key thenafter, put it in User Entity using #EmbeddedId and map both primary key using Hibernate Relational Mapping...
There are two option to Composite Primary Key:
Using #EmbeddedId
Using #IdClass()
Here down is example:
----------------------------------- Using EmbeddedId -----------------------------------
Compound primary key:
#Embeddable
public class UserIdName implements Serializable {
int id;
String name;
// getter and setter
}
User:
#Entity
#Table(name = "users")
public class USER{
#EmbeddedId
private UserIdName id;
String department;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Set<Userdetail> userdetail;
// getter and setter
}
UserDetails:
#Entity
#Table(name = "Userdetail")
public class Userdetail {
#Id
private int detail_id;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "id", referencedColumnName = "id"),
#JoinColumn(name = "name", referencedColumnName = "name") })
private USER user;
String address;
String otherFields;
// getter setter
}
----------------------------------- Using IdClass -----------------------------------
Compound primary key:
public class UserIdName implements Serializable {
int id;
String name;
// getter and setter
}
User:
#Entity
#Table(name = "users")
#IdClass(UserIdName.class)
public class USER{
#Id
int id;
#Id
String name;
String department;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Set<Userdetail> userdetail;
// getter and setter
}
UserDetails:
#Entity
#Table(name = "Userdetail")
public class Userdetail {
#Id
private int detail_id;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "id", referencedColumnName = "id"),
#JoinColumn(name = "name", referencedColumnName = "name") })
private USER user;
String address;
String otherFields;
// getter setter
}
-> If you wanna insert both foreign key manually try below code
Put this code in UserDetails
#ManyToOne
#JoinColumn(name = "id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "name", referencedColumnName = "name", insertable = false, updatable = false)
private USER user;
#Column(name="id")
private int id;
#Column(name="name")
private String name
// don't forget to put getter setter
User Table:
User Detail Table:
I am a beginner in JPA. As per requirement I am trying to fetch data from multiple tables (Feeder,Monthlyfeederdetails) and insert into another table (Report). Can someone please show me the way to do it. I have looked at a few sql examples like
INSERT INTO table2
(column_name(s))
SELECT column_name(s)
FROM table1;
but how do I reference the different tables in one repository?
I need to get the feeder_name, feeder_type from Feeder table
and duration_of_intr,no_of_afcon from Monthlyfeederdetails table
and also the the (duration_of_intr * no_of_afcon)
in the third table i.e Report table
Feeder Model
#Entity
public class Feeder {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private int feeder_id;
private String feeder_name;
private int feeder_type;
private int no_of_consumer_in_the_feeder;
}
Getters and setters
Monthlyfeederdetails Model
#Entity
public class Monthlyfeederdetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private int user_id;
private int feeder_id;
private int feeder_type;
//private int no_of_con_in_feeder;
private int duration_of_intr; //duration of interruption
private int freq_of_intr; //frequency of interruption;
private int no_of_afcon; //no of consumer affected due to shutdown
private int month;
private int year;
}
Get and Set
Report Model
#Entity
#Table(name= "report of caifi and caidi")
public class Report {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int reportid;
private String feeder_name;
private int feeder_type;
private int no_of_afcon;
private int duration_of_intr;
private int no_of_afconXduration_of_intr;
How can i achieve that?
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.
I have Entities (Student,StudentSkills, StudentEmpSkills)
Student.java
#Entity
#Table(name = "Student", catalog = "dbs")
public class Student implements java.io.Serializable {
private int id;
...
...
private Set<StudentSkills> studentSkills= new HashSet<StudentSkills>(0);
private Set<StudentEmpSkills> studentEmpSkills= new HashSet<StudentEmpSkills>(0);
#OneToMany(fetch = FetchType.EAGER, mappedBy = "Student")
public Set<StudentSkills> getStudentSkills() {
return this.studentEmpSkills;
}
public void setStudentSkills(
Set<StudentSkills> studentSkills) {
this.studentSkills = studentSkills;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "Student")
public Set<StudentEmpSkills> getStudentEmpSkills() {
return this.StudentEmpSkills;
}
public void setStudentEmpSkills(
Set<StudentEmpSkills> studentEmpSkills) {
this.studentEmpSkills= studentEmpSkills;
}
}
in StudentEmpSkills.java
#Entity
#Table(name = "studentEmpSkills", catalog = "npdbs")
public class StudentEmpSkills implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private StudentEmpSkillsId id;
private Student Student ;
......
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "studentID", nullable = false, insertable = false, updatable = false)
public Student getStudent() {
return student;
}
In the above we are getting the Set of StudentEmpSkils from Student by one to many and many to one relation.
as we are getting Student from StudentEmpSkill
In JoinColumn we are giving studentID column to fetch the abject.
Now I want to get StudentSkill object from StudentEmpSkills.
StudentSkills - (studentID* | skillID*) | skillname
StudentEmpSkills - (studentID* | skillID* | empID*) | empName
(..) - composit primary key
So I want to fetch StudentSkills from StudentEmpSkill
What I need to write in StudentEmpSkills to fetch StudentSkill. because we have composit primary key in StudentSkill.
How to map the StudentSkill to StudentEmpSkills.
Can anyone please suggest?
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<>();