Spring JPA 2 nested many to many relationship - spring

I am having a problem with my many to many relationship. Basically, here is what I am trying to do.
I have 2 many to many tables that are nested, as seen on the following image.
Db diagram
When compiling my code, I get the following error:
Caused by: java.sql.SQLSyntaxErrorException: Unknown column 'weeks0_.employee_project_employee_id' in 'field list'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120) ~[mysql-connector-java-8.0.18.jar:8.0.18]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.18.jar:8.0.18]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.18.jar:8.0.18]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.18.jar:8.0.18]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1003) ~[mysql-connector-java-8.0.18.jar:8.0.18]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-3.4.1.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-3.4.1.jar:na]
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57) ~[hibernate-core-5.4.6.Final.jar:5.4.6.Final]
So I understand that I have a problem with the weeks.
here are my entities:
Week:
package com.achsogo.rpt.entity;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name="week")
public class Week {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int weekNumber;
private int year;
#OneToMany(mappedBy = "week", cascade = CascadeType.ALL, orphanRemoval = true, fetch =
FetchType.EAGER)
private Set<EmployeeProjectWeek> employeeProjects;
public Week() {
}
public Week(int weekNumber, int year) {
this.weekNumber = weekNumber;
this.year = year;
} //Getter and Setters
Project:
import javax.persistence.*;
import java.util.*;
#Entity
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String alias;
private String pspElement;
private int contractValue;
private int plannedCost;
private int plannedHours;
private int assignedHours;
private Date startDate;
private Date endDate;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "project", orphanRemoval = true, fetch =
FetchType.EAGER)
private Set<ProjectAP> projectAPS;
#ManyToOne
private Department department;
#OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true, fetch =
FetchType.EAGER)
private Set<EmployeeProject> employees;
//Empty constructor
public Project(){}
//constructor for new project with default names
public Project(String alias){
this.alias = alias;
}
Employee:
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
private int employeeNumber;
private String acronym;
private int availability;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true, fetch =
FetchType.EAGER)
private Set<EmployeeProject> projects;
#ManyToOne
private Department department;
#ManyToOne
private Role role;
Table between Project and Employee (with the class to create the ID):
EmployeeProjectID:
#Embeddable
public class EmployeeProjectId implements Serializable {
private Long projectId;
private Long employeeId;
private EmployeeProjectId(){}
public EmployeeProjectId(Long projectId, Long employeeId){
this.employeeId = employeeId;
this.projectId = projectId;
}
EmployeeProject: It is here that the weeks are being called (If I am not mistaken)
#Entity
#Table(name = "project_employee")
public class EmployeeProject {
#EmbeddedId
private EmployeeProjectId id;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("projectId")
private Project project;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("employeeId")
private Employee employee;
#Column(name = "amount_time")
private int amountTime = 0;
#Column(name = "is_project_leader")
private boolean isProjectLeader = false;
#OneToMany(mappedBy = "employeeProject", cascade = CascadeType.ALL, orphanRemoval = true, fetch =
FetchType.EAGER)
private Set<EmployeeProjectWeek> weeks;
public EmployeeProject(){}
And now the table between Week and Employee Project:
EmployeeProjectWeekId: To create the ID for the nested many to many table:
#Embeddable
public class EmployeeProjectWeekId implements Serializable {
private EmployeeProjectId employeeProjectId;
private Long weekId;
private EmployeeProjectWeekId(){}
And finally the EmployeeProjectweek:
#Entity
#Table(name="project_employee_week")
public class EmployeeProjectWeek {
#EmbeddedId
private EmployeeProjectWeekId id;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("weekId")
private Week week;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("employeeProjectId")
private EmployeeProject employeeProject;
#Column(name="amount_time")
private int amountTime = 0;
#Column(name="is_project_leader")
private boolean isProjectLeader= false;
public EmployeeProjectWeek(Week week, EmployeeProject employeeProject) {
this.week = week;
this.employeeProject = employeeProject;
this.id = new EmployeeProjectWeekId(employeeProject.getId(), week.getId());
}
Does anyone has a clue where I have done an error? Thanks in advance for your time!

It was a naming problem. I solved this by dropping all the tables in my DB and having Spring to automatically generate the tables. Now everything works well.
I entered the following lines in my application.properties:
spring.jpa.hibernate.ddl=true
spring.jpa.hibernate.ddl-auto=update

Related

Add extra custom column to auto mapped Table in Spring JPA ManyToMany

#Getter #Setter #NoArgsConstructor #AllArgsConstructor
#Table(name = "my_users")
public class MyUsers {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private Long id;
#Column(nullable = false, unique = true)
private String userName;
private String password;
#ManyToMany
private List<MyUsers> connections;
}
This is my MyUsers Model Class. I am using Hibernate and MySQL.
#ManyToMany
private List<MyUsers> connections;
This ManyToMany relationship is automatically creating the table 'my_users_connections' with 'my_users_id' and 'connections_id' colums. How can I add extra columns to this auto mapped table?
It's not ideal solution...
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "my_users")
public class MyUsers implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private Long myUsersId;
#Column(nullable = false, unique = false)
private String userName;
private String password;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "my_users_connections",
joinColumns = { #JoinColumn(name = "my_users_id") },
inverseJoinColumns = { #JoinColumn(name = "connections_id") })
private List<MyUsers> connections;
}
Create embedded id MyUsersConnectionsPK:
#Data
#Embeddable
public class MyUsersConnectionsPK implements Serializable {
#Column(name = "my_users_id")
private Long myUsersId;
#Column(name = "connections_id")
private Long connectionsId;
}
Create MyUsersConnections, which represent ManyToMany
#Data
#Entity
#Table(name = "my_users_connections")
public class MyUsersConnections implements Serializable {
#EmbeddedId
private MyUsersConnectionsPK id;
#ManyToOne
#MapsId("my_users_id")
#JoinColumn(name = "my_users_id")
private MyUsers myUsersId;
#ManyToOne
#MapsId("connections_id")
#JoinColumn(name = "connections_id")
private MyUsers connectionsId;
#Column(name = "extra_column")
private String extraColumn;
}
Create JPA repository
#Repository
public interface MyUsersConnectionsRepository extends JpaRepository<MyUsersConnections, MyUsersConnectionsPK> {
List<MyUsersConnections> findMyUsersConnectionsByMyUsersIdMyUsersId(Long id);
}
And simple sample for using:
#Service
public class Test {
#Autowired
private MyUsersConnectionsRepository myUsersConnectionsRepository;
#Autowired
private MyUsersRepository myUsersRepository;
public void test() {
MyUsers myUsers = new MyUsers();
myUsers.setUserName("user name");
myUsers.setPassword("password");
MyUsers myUsers2 = new MyUsers();
myUsers2.setUserName("user name 2");
myUsers2.setPassword("password 2");
myUsers.setConnections(Collections.singletonList(myUsers2));
myUsers = myUsersRepository.saveAndFlush(myUsers);
List<MyUsersConnections> myUsersConnections = myUsersConnectionsRepository.findMyUsersConnectionsByMyUsersIdMyUsersId(myUsers.getMyUsersId());
MyUsersConnections item = myUsersConnections.get(0);
item.setExtraColumn("Extra column");
myUsersConnectionsRepository.saveAndFlush(item);
}
}

(Do not display relationship values)

I have two entity with name of the article and article Category.
and they have one-to-many relationships.
I use #JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,property = "id")
but I cant see data of article category(category_id) in spring data rest.
ArticleCategory.class
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory", fetch = FetchType.LAZY)
private Set<Article> articles = new HashSet<>();
}
Article.class
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable {
public Article() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
#Column(name = "title")
private String title;
#Column(name = "image_url")
private String image_url;
#Column(name = "short_description")
private String short_description;
#Column(name = "text")
private String text;
#Column(name = "keywords", nullable = true)
private String keywords;
#Column(name = "visit", nullable = false)
private int visit;
#Column(name = "code", nullable = false)
private UUID code;
#Column(name = "date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name = "date_updated", nullable = false)
#UpdateTimestamp
private Date dateUpdated;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
public Article(String title, String image_url, String short_description, String text, String keywords, int visit, UUID code) {
this.title = title;
this.image_url = image_url;
this.short_description = short_description;
this.text = text;
this.keywords = keywords;
this.visit = visit;
this.code = code;
}
}
Article Repository
#CrossOrigin("http://localhost:4200")
#RepositoryRestResource(collectionResourceRel = "article", path = "article")
public interface ArticleRepository extends JpaRepository<Article,Long> {
Article findByCode(UUID uuid);
}
And this is output of spring data rest
enter image description here
That is exactly because you used #JsonManagedReference and #JsonBackReference. Keep in mind the following when using them:
#JsonManagedReference is the forward part of the relationship and is the one that gets serialized normally.
#JsonBackReference is the back part of the relationship and it will be omitted from serialization.
The serialized Article object does not contain a reference to the ArticleCategory object.
If you want to have any ArticleCategory data when serializing Article you can either use #JsonIdentityInfo so that one of the properties is serialized (in this case I've chosen id for both):
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
}
#Entity
#Table(name = "article_category")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If you are only interested in categoryId another possibility would be to use #JsonIgnore on private Set<Article> articles property so that it is not serialized:
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If none of those suits your needs you might need to implement your own custom serializer. You can read more about all those options at https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
I solved the problem using the controller
And that's why #JsonManageRefrence and #JsonBackRefrence do not work
I replaced the lazy load with the eager load in both entity
#ManyToOne(fetch = FetchType.Eager)
#JoinColumn(name = "user_id")
#JsonManageRefrence
private User user;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory",
fetch = FetchType.Eager)
#JsonBackRefrence
private Set<Article> articles = new HashSet<>();
and then add a controller
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/getAllArticle")
public class MyController {
private ArticleRepository articleRepository;
// you must do constructor injection
#GetMapping("/getAllArticle")
public List<Article> allArticle()
{
return articleRepository.findAll();
}
}

Spring Data Projection with OneToMany error

I have a entity call Circuit.
#Entity
public class Circuit implements Comparable<Circuit>, Serializable {
#Column
private String id;
#OneToMany(mappedBy = "circuit", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<Step> workflow = new HashSet<>();
...
}
I have a class called CircuitLight
public class CircuitLight {
private String id;
private Set<Step> workflow;
/* constructor, getters and setters */
}
In my CircuitRepository, i'm trying to make a projection
#Transactional(readOnly = true)
#Query("select new com.docapost.circuit.CircuitLight(c.id, c.workflow) from Circuit c where c.account.siren = :siren")
Set<CircuitLight> findAllByAccountSirenProjection(#Param("siren") String siren);
When i execute, i have a error message:
could not extract ResultSet; SQL [n/a] com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'circuit0_.id' in 'on clause'
I try with other entity. Every time i have a property with a relation #OneToMany, i have the issue...
Is it possible to make a projection with class (Without use a interface) when there are a relation OneToMany ?
UPDATE:
Step.class
#Entity
public class Step implements Comparable<Step>, Serializable {
private static final List<String> INDEXABLE_PROCESSES = Arrays.asList(
ParapheurWorkflowModel.SERVER,
ParapheurWorkflowModel.SIGN,
ParapheurWorkflowModel.VISA
);
#Id
#GeneratedValue
#Expose
#SerializedName("step_id")
public long id;
#ManyToOne
public Circuit circuit;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(joinColumns = #JoinColumn(name = "step_id"), inverseJoinColumns = #JoinColumn(name = "technicalGroup_id"))
private List<TechnicalGroup> technicalGroups = new ArrayList<>();
#Column(name = "step_type", nullable = false)
#Expose
#SerializedName("subprocess_ref")
public String type;
#Column(nullable = false)
public int orderIndex;
/* contructor, getters and setters */
}
UPDATE 2:
Hum.... My bad, in my circuit class, i have a EmbeddedId
#EmbeddedId
private CircuitPK key;
#Embeddable
public static class CircuitPK implements Serializable {
public String id;
public String siren;
}
I try with this code in Step.class
#ManyToOne
#JoinColumns(value = {
#JoinColumn(name = "circuit_siren", referencedColumnName = "siren"),
#JoinColumn(name = "circuit_id", referencedColumnName = "id")
})
public Circuit circuit;
The result is the same
Write the following code in the Step entity
#ManyToOne
#JoinColumn(name="id", nullable=false)
private Circuit circuit;

SQL error or missing database / OneToMany SQLIte-Spring Boot

I have a problem with the relationship oneToMany. I created tables in SQLite DB, this is my tables:
My CategoryModel:
#Entity
#Table(name = "Category")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class CategoryModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String category_name;
private String category_description;
private String image_path;
#JsonIgnore
#OneToMany( mappedBy = "category")
private Set<ProductModel> category;
My ProducCategory:
#Entity
#Table(name = "Product_Category")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class ProductModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long product_id;
private Long category_id;
private String name;
private String description;
private int numberOfProduct;
private String image;
private int price;
#ManyToOne
#JoinColumn(name = "country_id", nullable = false)
private CategoryModel category;
I can get data from the Category table well but when I call data from the Product_Category table I have the error:
SQL error or missing database (no such column: productmod0_.country_id)
country_id does not exist anywhere in your tables.
What you want is : #JoinColumn(name = "category_id", nullable = false)

Spring JPA: How to insert data to join many tables with #ManytoMany relationship

I'm starting to learn Spring Java Framework . I created some Enity to join 2 Model like my Database. And now I want to insert to Join Table by JpaRepository. What i have to do?
This is my Code (Please fix help me me if something is not right)
Model Users_RoomId to define Composite Primary Key
#Embeddable
public class Users_RoomId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "ID_room", nullable = false)
private String idRoom;
#Column(name = "user_id", nullable = false)
private int idUser;
}
Model Users_Room to join 2 Model Users and Room
#Entity
#Table(name ="bookroom")
public class Users_Room {
#EmbeddedId
private Users_RoomId usersroomId;
#ManyToOne
#MapsId("idRoom")
private Room room;
#ManyToOne
#MapsId("idUser")
private Users users;
#Column(name = "Bookday")
private String bookday;
Model Users and Room I used annotation #OneToMany
Model Users
#Entity
#Table(name = "users")
public class Users implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "email")
private String email;
#Column(name = "pass")
private String pass;
#Column(name = "role")
private int role;
#OneToMany(mappedBy = "users")
private List<Users_Room> user;
Model Room
#Entity
#Table(name ="room")
public class Room implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID_room", nullable = false)
private String id;
#Column(name = "name_room", nullable = false)
private String name;
#Column(name = "Description")
private String describe;
#ManyToOne
#JoinColumn(name = "ID_status")
private Status status;
#Column(name = "room_image")
private String image;
public Room() {
super();
}
#ManyToOne
#JoinColumn(name = "ID_kind")
private KindRoom kind;
#OneToMany(mappedBy = "room")
private List<Users_Room> rooms;
This is my database
So I don't know how to insert a new bookroom with iduser,idroom and bookday with JPA repository.. It'necessary to write Query in JPARepository or We just need to use method save() to insert data
Thanks everyone
I had same problem and solved with following code. I used method save() to insert data. Following code is 'createRoom' method in 'RoomService.java'.
RoomService.java
private final RoomRepository roomRepository;
private final UserRoomRepository userRoomRepository;
private final UserRepository userRepository;
public RoomService(RoomRepository roomRepository, UserRoomRepository userRoomRepository, UserRepository userRepository) {
this.roomRepository = roomRepository;
this.userRoomRepository = userRoomRepository;
this.userRepository = userRepository;
}
#Transactional
public RoomDto createRoom(Long userId, Long chattingUserId) {
Room room = roomRepository.save(new Room());
room.addUserRoom(userRepository.findById(userId).orElseThrow(()->new NoSuchElementException("No User")));
room.addUserRoom(userRepository.findById(chattingUserId).orElseThrow(()->new NoSuchElementException("No User")));
userRoomRepository.save(new UserRoom(userRepository.findById(userId).orElseThrow(()->new NoSuchElementException("No User")),room));
userRoomRepository.save(new UserRoom(userRepository.findById(chattingUserId).orElseThrow(()->new NoSuchElementException("No User")),room));
RoomDto roomDto = RoomDto.of(room);
return roomDto;
}

Resources