JPA #ForeignKey(value = ConstraintMode.NO_CONSTRAINT) not working with #ManyToMany - spring-boot

I have two entities with ManyToMany Relationship. Goal here is to create schema when application start with no foreign key
1). Job.java
package com.govjobportalbackend.entity;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "job")
public class Job extends BasicEntity {
#Column(name = "icon")
private String icon;
#ManyToMany
#JoinTable(
name="job_city",
joinColumns = #JoinColumn(name = "job_id"),
inverseJoinColumns = #JoinColumn(name = "city_id"),
foreignKey = #ForeignKey(value = ConstraintMode.NO_CONSTRAINT),
inverseForeignKey = #ForeignKey(value = ConstraintMode.NO_CONSTRAINT)
)
private List<City> cities;
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
}
2). City.java
package com.govjobportalbackend.entity;
import java.util.List;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
#Entity
#DiscriminatorValue(value = "city")
public class City extends JobMetadata {
#ManyToMany(mappedBy = "cities")
private List<Job> jobs;
#Override
public List<Job> getJobs() {
return jobs;
}
#Override
public void setJobs(List<Job> jobs) {
this.jobs = jobs;
}
}
Below property is set in application.properties file
spring.jpa.hibernate.ddl-auto=update
When run the application, it is logging below SQLs in logs and is creating two foreign keys
Hibernate: create table job (id int4 not null, name varchar(255), icon varchar(255), primary key (id))
Hibernate: create table job_city (job_id int4 not null, city_id int4 not null)
Hibernate: create table job_metadata (type varchar(31) not null, id int4 not null, name varchar(255), primary key (id))
Hibernate: alter table if exists job_city add constraint FKiksm0d31mc3osxut4ciaf4uof foreign key (job_id) references job
Hibernate: alter table if exists job_city add constraint FKknw4pf63xt1tvnqrmrjrm5hqq foreign key (city_id) references job_metadata
If I annotate as per below in City.java then it works as expected but as per my "little" research, this bug is fixed in hibernate (so mapped entity is not required to be annotated with depreciated annotation), or may be I am wrong.
#ManyToMany(mappedBy = "cities")
#org.hibernate.annotations.ForeignKey(name = "none")
private List<Job> jobs;
Environment I am using is below;
Java 11
Hibernate 5.4.28.Final (spring-boot-starter-web)

As SimonMartinelli pointed out, this is most definitely a Hibernate bug. The version that worked for me was:
#JoinTable(
name="job_city",
joinColumns = #JoinColumn(name = "job_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)),
inverseJoinColumns = #JoinColumn(name = "city_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
)
I found that the functionality breaks when you either (1) use #JoinTable.foreignKey instead, or (2) omit the name parameter.

Related

How to add the IDs of foreign tables to another table passing the parameters by constructor?

Hi everyone I'm working with SpringBoot and I want to send the ID's of table Producto and Cliente to Pedidos, I'm using the constructor for to pass of parametrs
I tried to create a List as String to hold the values ​​and then use it to send the data to the other method
Class Product
package com.example.demo.model;
import java.util.Set;
import javax.persistence.*;
#Entity
#Table(name = "Productos")
public class Producto {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombreProducto;
private String precioProducto;
/*Here i send of FK of this table to Pedidos*/
#OneToMany(mappedBy = "producto",cascade = CascadeType.ALL)
private Set<Pedido> pedidos;
public Producto(String nombreProducto, String precioProducto) {
this.nombreProducto = nombreProducto;
this.precioProducto = precioProducto;
}
//Getters and Setters
}
Class Cliente
package com.example.demo.model;
import java.util.Set;
import javax.persistence.*;
#Entity
#Table(name="Clientes")
public class Cliente {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombreCliente;
private String correoElectronico;
/*Here i send of FK of this table to Pedidos*/
#OneToMany(mappedBy = "cliente",cascade = CascadeType.ALL)
private Set<Pedido> pedidos;
public Cliente(String nombreCliente, String correoElectronico) {
this.nombreCliente = nombreCliente;
this.correoElectronico = correoElectronico;
}
//Getters and Setters
}
Class Pedido
package com.example.demo.model;
import javax.persistence.*;
#Entity
#Table(name = "Pedido")
public class Pedido {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String fechaPedido;
private String direccion;
/*
Here I create the atribute of FK of the tables Cliente and Producto
*/
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cliente_id", referencedColumnName = "id")
private Cliente cliente;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "producto_id", referencedColumnName = "id")
private Producto producto;
public Pedido(String fechaPedido, String direccion, Cliente cliente, Producto producto) {
this.fechaPedido = fechaPedido;
this.direccion = direccion;
this.cliente = cliente;
this.producto = producto;
}
//Getters and Setters
}
And the last Class it's the RunnClass
package com.example.demo;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
//import java.util.stream.Stream;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.example.demo.model.Cliente;
import com.example.demo.model.Pedido;
import com.example.demo.model.Producto;
import com.example.demo.repository.ClienteRepository;
import com.example.demo.repository.PedidosRepository;
import com.example.demo.repository.ProductoRepositroy;
import com.github.javafaker.Faker;
#Component
public class SampleDataLoader implements CommandLineRunner {
private final ClienteRepository clienteRepository;
private final ProductoRepositroy productoRepositroy;
private final PedidosRepository pedidosRepository;
private final Faker faker; //It's a ASI of DataFaker
public SampleDataLoader(ClienteRepository clienteRepository,
ProductoRepositroy productoRepositroy,
PedidosRepository pedioPedidosRepository) {
this.clienteRepository = clienteRepository;
this.productoRepositroy = productoRepositroy;
this.pedidosRepository = pedioPedidosRepository;
this.faker = new Faker(); //It's a ASI of DataFaker
}
#Override
public void run(String... args) throws Exception {
ejecutarClases();
}
private void ejecutarClases() {
List<Cliente> clientes = IntStream.rangeClosed(1, 20)
.mapToObj(i -> new Cliente(faker.name().fullName(),
faker.internet().emailAddress()))
.collect(Collectors.toList());
clienteRepository.saveAll(clientes);
List<Producto> productos = IntStream.rangeClosed(1, 100)
.mapToObj(i -> new Producto(faker.commerce().productName(), "$"+faker.commerce().price()))
.collect(Collectors.toList());
productoRepositroy.saveAll(productos);
//I don't know how to send two ID's to this table,
//if you can see I have two values as null
//I want to send the ID's the other tables
List<Pedido> pedidos = IntStream.rangeClosed(1, 30)
.mapToObj(i -> new Pedido(faker.backToTheFuture().date(),
faker.address().streetAddress(), null, null))
.collect(Collectors.toList());
pedidosRepository.saveAll(pedidos);
}
}
I hope someone can help me please.

why employee_id and department_id not update automatic in database Hibernate jpa

few days back i starting learning hibernate JPA but i am unable to find solution of given problem below
My Project consist three class employee ,phone ,department by seeing code you can easily understand what i am doing .
Main problem raise when i try to save this data into database using spring boot controller it showing null value in column . In employee table department_id is null(not automatic update using cascade.All) same in phone table employee_id is null.
I do not want update manually .is their any way so dep_id and emp_id automatic update to foreign key table .
{
"name":"CSE",
"employees":[
{
"name":"Welcome",
"age":23,
"phones":[{"number":1234567890},{"number":1234567890}]
},
{
"name":"back",
"age":25,
"phones":[{"number":1234567890},{"number":1234567890}]
}
]
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private int age;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
#OneToMany(mappedBy = "employee", cascade=CascadeType.ALL)
private List<Phone> phones;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*
#Entity
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String number;
#ManyToOne
#JoinColumn(name = "employee_id")
private Employee employee;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
}
dept table in database
id name
1 CSE
employee table
id name age department_id
1. welcome 23. null
2. back. 25. null
phone table
id number employee_id
1. 1234567890. null
2. 1234567890. null
3. 1234567890 null
4. 1234567890. null
Why employee_id and department_id not updating automatic in cascade All
Controller class
package com.example.controller;
import com.example.dao.DepRepo;
import com.example.dao.EmployeeRepo;
import com.example.dao.PhoneRepo;
import com.example.entity.Department;
import com.example.entity.Employee;
import com.example.service.FakeService;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Inject;
#Controller("/dummy")
public class DummyController {
#Inject
FakeService fakeService;
#Inject
PhoneRepo phoneRepo;
#Inject
EmployeeRepo employeeRepo;
#Inject
DepRepo depRepo;
#Get ("/")
public String fun(){
fakeService.fun();
return "welcome back";
}
#Post("/add")
public HttpResponse<?> fun(#Body Department dep){
System.out.println(dep);
depRepo.save(dep);
return HttpResponse.status(HttpStatus.ACCEPTED).body("data add successfully");
}
}
Your Hibernate mapping says that the relationships are mapped by the 'many' side of the association:
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
So Hibernate looks for the value of the 'department' in the employee entity and it is null (because there's no value in the JSON data)
So try removing mappedBy to tell Hibernate that the relationship is mapped on the 'one' side

EntityManager Configuration to Fetch Data Oracle DB - SpringBoot [duplicate]

I'm following the Learn Spring 5 etc on udemy and I'm at the part where we test our application. Everything worked fine till now, i was able to connect to the postgreSQL database and all but now I'm stuck at this test failing since 2 days.
I don't understand what is causing the Test to fail. The application run but the test doesn't. Here it is the test class:
package com.ghevi.dao;
import com.ghevi.pma.ProjectManagementApplication;
import com.ghevi.pma.dao.ProjectRepository;
import com.ghevi.pma.entities.Project;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlGroup;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
#ContextConfiguration(classes= ProjectManagementApplication.class)
#RunWith(SpringRunner.class)
#DataJpaTest // for temporary databases like h2
#SqlGroup({
#Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:schema.sql", "classpath:data.sql"}),
#Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = "classpath:drop.sql")
})
public class ProjectRepositoryIntegrationTest {
#Autowired
ProjectRepository proRepo;
#Test
public void ifNewProjectSaved_thenSuccess(){
Project newProject = new Project("New Test Project", "COMPLETE", "Test description");
proRepo.save(newProject);
assertEquals(5, proRepo.findAll().size());
}
}
And this is the stack trace:
https://pastebin.com/WcjNU76p
Employee class (don't mind the comments, they are probably garbage):
package com.ghevi.pma.entities;
import javax.persistence.*;
import java.util.List;
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "employee_seq") // AUTO for data insertion in the class projmanagapplication (the commented out part), IDENTITY let hibernate use the database id counter.
private long employeeId; // The downside of IDENTITY is that if we batch a lot of employees or projects it will be much slower to update them, we use SEQUENCE now that we have schema.sql (spring does batch update)
private String firstName;
private String lastName;
private String email;
// #ManyToOne many employees can be assigned to one project
// Cascade, the query done on projects it's also done on children entities
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST}, // Standard in the industry, dont use the REMOVE (if delete project delete also children) or ALL (because include REMOVE)
fetch = FetchType.LAZY) // LAZY is industry standard it loads project into memory, EAGER load also associated entities so it slows the app, so we use LAZY and call child entities later
//#JoinColumn(name="project_id") // Foreign key, creates a new table on Employee database
#JoinTable(name = "project_employee", // Merge the two table using two foreign keys
joinColumns = #JoinColumn(name="employee_id"),
inverseJoinColumns = #JoinColumn(name="project_id"))
private List<Project> projects;
public Employee(){
}
public Employee(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public List<Project> getProjects() {
return projects;
}
public void setProjects(List<Project> projects) {
this.projects = projects;
}
/* Replaced with List<Project>
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
*/
public long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(long employeeId) {
this.employeeId = employeeId;
}
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Also this is the schema.sql where i reference those sequences, since this file is run by the test, i have just noticed that IntelliJ mark some errors in this file. For example it mark red some spaces and the T of TABLE saying:
expected one of the following: EDITIONING FORCE FUNCTION NO OR PACKAGE PROCEDURE SEQUENCE TRIGGER TYPE VIEW identifier
CREATE SEQUENCE IF NOT EXISTS employee_seq;
CREATE TABLE IF NOT EXISTS employee ( <-- here there is an error " expected: "
employee_id BIGINT NOT NULL DEFAULT nextval('employee_seq') PRIMARY KEY,
email VARCHAR(100) NOT NULL,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
CREATE SEQUENCE IF NOT EXISTS project_seq;
CREATE (the error i described is here -->) TABLE IF NOT EXISTS project (
project_id BIGINT NOT NULL DEFAULT nextval('project_seq') PRIMARY KEY,
name VARCHAR(100) NOT NULL,
stage VARCHAR(100) NOT NULL,
description VARCHAR(500) NOT NULL
);
CREATE TABLE IF NOT EXISTS project_employee ( <--Here again an error "expected:"
project_id BIGINT REFERENCES project,
employee_id BIGINT REFERENCES employee
);
You never tell it to about the sequence, just what the generator is called
Try
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "employee_generator")
#SequenceGenerator(name = "employee_generator", sequenceName = "employee_seq", allocationSize = 1)
I had the same issue. Adding the below annotation resolved it. #SequenceGenerator(name = "employee_seq", allocationSize = 1)
Perhaps, there is something wrong with the generator definition in the employee entity.
The "generator" must be the "name" of the SequenceGenerator, not the name of other things such as the sequence. Maybe Because you gave the name of the sequence, and did not have a generator with that name it used the default preallocation which is 50.
Also, the strategy should be SEQUENCE, but isn't required if you define the generator, it is only relevant when you don't define the generator.
By default allocationSize parameter in SequenceGenerator is set to be 50. This problem arises when your sequence increment mismatches. You can either change the sequence increment value or assign allocationSize size as per your requirement.

findByForeign key in Spring boot

I have added a foreign key as such:
#ManyToOne
#JoinColumn
private UserEntity userEntity;
And my table has these columns:
CREATE TABLE `web_course` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`coursename` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`user_entity_user_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK6eys4s4qx87rxo0ha68q05oc8` (`user_entity_user_id`),
CONSTRAINT `FK6eys4s4qx87rxo0ha68q05oc8` FOREIGN KEY (`user_entity_user_id`) REFERENCES `web_user` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
I would like to create a method to findByUserId in:
#Repository
public interface CourseRepository extends JpaRepository<CourseEntity, Long> {
I have tried several combinations in vain:
public CourseEntity findByUserId(int user_id)
public CourseEntity findByUserEntityUserId(int user_id)
public CourseEntity findBy_User_entity_user_id(int user_id)
I read that there is a naming convention but I can't seem to find the correct convention since I am getting:
Error creating bean with name 'courseController': Unsatisfied dependency expressed through field 'courseRepository';
Course class:
package com.finaly.projectback.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#Entity
#Table(name = "web_course")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class CourseEntity {
#ManyToOne
#JoinColumn
private UserEntity userEntity;
public UserEntity getUserEntity() {
return userEntity;
}
public void setUserEntity(UserEntity userEntity) {
this.userEntity = userEntity;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "coursename")
private String coursename;
#Column(name = "description")
private String description;
public CourseEntity() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCoursename() {
return coursename;
}
public void setCoursename(String coursename) {
this.coursename = coursename;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public CourseEntity(UserEntity userEntity, long id, String coursename, String description) {
super();
this.userEntity = userEntity;
this.id = id;
this.coursename = coursename;
this.description = description;
}
#Override
public String toString() {
return "CourseEntity [userEntity=" + userEntity + ", id=" + id + ", coursename=" + coursename + ", description="
+ description + "]";
}
}
Can someone point me in the right direction please?
Following code should work:
public CourseEntity findByUserEntity_UserId(int user_id)
Check logs to see if the course repository is instantiated or not. Error indicates controller can'be created because of the dependency issue.
Error creating bean with name courseController: Unsatisfied dependency expressed through field courseRepository

hibernate #ManyToOne no table relationship

Currently Using:
Hibernate 4.0.1.Final
Spring-data-jpa: 1.0.3.RELEASE
QueryDSL: 2.3.0
MySQL 5.x
I have an interesting problem that I have not found the answer, or clue for yet. I have two tables that did not have foreign key or other relationship. But to try and solve this issue I added one. I want my User entity to hold it's UserRole. This pattern is repeated throughout the database, but this is the easiest to describe.
Here are my tables:
User
userId bigint(20) PK
password varchar(255)
status int(11)
userName varchar(255)
userRoleId long
CONSTRAINT `FK_USERROLE` FOREIGN KEY (`userRoleId`) REFERENCES `UserRole` (`userRoleId`)
UserRole
userRoleId bigint(20) PK
userRoleDescription varchar(255)
userRoleDescriptionShort varchar(255)
Here are my classes:
User.java
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#XmlRootElement(name = "User")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
private String password;
private int status;
private String userName;
#ManyToOne
#JoinColumn(name = "userRoleId")
private UserRole userRole;
public UserRole getUserRole() {
return userRole;
}
public void setUserRole(UserRole userRole) {
this.userRole = userRole;
}
UserRole.java
#Entity
#XmlRootElement(name = "userRole")
public class UserRole {
private Long userRoleId;
private String userRoleDescription;
private String userRoleDescriptionShort;
#ElementCollection
#OneToMany(mappedBy = "userRole")
private List<User> users;
public UserRole() {...}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getUserRoleId() {... }
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}
So you can see where I am trying to associate the UserRole.userRoleId with the User. I thought perhaps Hibernate would build the mapping and retrieve/associate the UserRole whenever the User was updated.
I have gone back and edited this post to use a foreign key between the tables, but on app server startup I get this:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: UserRole, for columns: [org.hibernate.mapping.Column(users)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:304)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:288)
at org.hibernate.mapping.Property.isValid(Property.java:216)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:467)
at org.hibernate.mapping.RootClass.validate(RootClass.java:268)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1287)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1729)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:84)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
I looked for that error, which appears related to JPA and transient variables, which is not the case here.
If i were you i would first clean the annotations, it is forbidden to annotate BOTH getter AND fields in the same entity, it could end up in unexpected results ...
#ElementCollection
#OneToMany(mappedBy = "userRole")
private List<User> users;
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}
should be simplified in :
#ElementCollection
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}

Resources