Unable to create a JOIN using Spring Data JPA - java

I'm new to Spring Data and tried to solve the issue by following this post on SO and some other tutorials but without much success.
I'm trying to make a simple Join using Spring Data JPA between 2 tables.
The tables in the database are called:
* user_vehicle -> contains information about all vehicles per user since 1 user can have many vehicles
* vehicle_model which contains data about vehicle models (id, name, etc)
Current data in the database in the user_vehicle table:
ID | vehicle_id | user_id
1 | 1 | 1
2 | 2 | 1
Here is the code I've tried but can't get it to work (getters and setters are removed from the post to shorten it):
#Entity(name = "vehicle_model")
public class VehicleModel {
#Id
#Min(1)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long manufacturerId;
private String title;
private int ccm;
private int kw;
private int yearOfManufacture;
private int engineTypeId;
private boolean isActive;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "vehicle_id", insertable = false, updatable = false)
#Fetch(FetchMode.JOIN)
private UserVehicle userVehicle;
}
#Entity(name = "user_vehicle")
public class UserVehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private long vehicleId;
#Column(nullable = false)
private long userId;
#OneToMany(targetEntity = VehicleModel.class, mappedBy = "userVehicle", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<VehicleModel> vehicleModels;
}
#Repository
public interface UserVehicleRepository extends CrudRepository<UserVehicle, Long>
{
Iterable<UserVehicle> findVehicleModelsByUserId(Long userId);
}
I'm expecting to get 2 results in the iterable with filled vehicle_model data. Instead I get 2 results but for the vehicleModels property I get "Unable to evaluate the expression Method threw 'org.hibernate.exception.SQLGrammarException' exception."
Here is the output from the console:
2019-06-23 02:04:10.988 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select uservehicl0_.id as id1_1_, uservehicl0_.user_id as user_id2_1_, uservehicl0_.vehicle_id as vehicle_3_1_ from user_vehicle uservehicl0_ where uservehicl0_.user_id=?
2019-06-23 02:04:11.034 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select vehiclemod0_.vehicle_id as vehicle_9_4_0_, vehiclemod0_.id as id1_4_0_, vehiclemod0_.id as id1_4_1_, vehiclemod0_.ccm as ccm2_4_1_, vehiclemod0_.engine_type_id as engine_t3_4_1_, vehiclemod0_.is_active as is_activ4_4_1_, vehiclemod0_.kw as kw5_4_1_, vehiclemod0_.manufacturer_id as manufact6_4_1_, vehiclemod0_.title as title7_4_1_, vehiclemod0_.vehicle_id as vehicle_9_4_1_, vehiclemod0_.year_of_manufacture as year_of_8_4_1_ from vehicle_model vehiclemod0_ where vehiclemod0_.vehicle_id=?
2019-06-23 02:04:11.035 WARN 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-06-23 02:04:11.035 ERROR 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'vehiclemod0_.vehicle_id' in 'field list'
2019-06-23 02:04:11.036 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select vehiclemod0_.vehicle_id as vehicle_9_4_0_, vehiclemod0_.id as id1_4_0_, vehiclemod0_.id as id1_4_1_, vehiclemod0_.ccm as ccm2_4_1_, vehiclemod0_.engine_type_id as engine_t3_4_1_, vehiclemod0_.is_active as is_activ4_4_1_, vehiclemod0_.kw as kw5_4_1_, vehiclemod0_.manufacturer_id as manufact6_4_1_, vehiclemod0_.title as title7_4_1_, vehiclemod0_.vehicle_id as vehicle_9_4_1_, vehiclemod0_.year_of_manufacture as year_of_8_4_1_ from vehicle_model vehiclemod0_ where vehiclemod0_.vehicle_id=?
2019-06-23 02:04:11.037 WARN 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-06-23 02:04:11.037 ERROR 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'vehiclemod0_.vehicle_id' in 'field list'
2019-06-23 02:04:11.038 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select vehiclemod0_.vehicle_id as vehicle_9_4_0_, vehiclemod0_.id as id1_4_0_, vehiclemod0_.id as id1_4_1_, vehiclemod0_.ccm as ccm2_4_1_, vehiclemod0_.engine_type_id as engine_t3_4_1_, vehiclemod0_.is_active as is_activ4_4_1_, vehiclemod0_.kw as kw5_4_1_, vehiclemod0_.manufacturer_id as manufact6_4_1_, vehiclemod0_.title as title7_4_1_, vehiclemod0_.vehicle_id as vehicle_9_4_1_, vehiclemod0_.year_of_manufacture as year_of_8_4_1_ from vehicle_model vehiclemod0_ where vehiclemod0_.vehicle_id=?
2019-06-23 02:04:11.039 WARN 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-06-23 02:04:11.040 ERROR 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'vehiclemod0_.vehicle_id' in 'field list'
2019-06-23 02:04:11.042 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select vehiclemod0_.vehicle_id as vehicle_9_4_0_, vehiclemod0_.id as id1_4_0_, vehiclemod0_.id as id1_4_1_, vehiclemod0_.ccm as ccm2_4_1_, vehiclemod0_.engine_type_id as engine_t3_4_1_, vehiclemod0_.is_active as is_activ4_4_1_, vehiclemod0_.kw as kw5_4_1_, vehiclemod0_.manufacturer_id as manufact6_4_1_, vehiclemod0_.title as title7_4_1_, vehiclemod0_.vehicle_id as vehicle_9_4_1_, vehiclemod0_.year_of_manufacture as year_of_8_4_1_ from vehicle_model vehiclemod0_ where vehiclemod0_.vehicle_id=?
2019-06-23 02:04:11.043 WARN 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-06-23 02:04:11.043 ERROR 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'vehiclemod0_.vehicle_id' in 'field list'
2019-06-23 02:04:11.045 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select vehiclemod0_.vehicle_id as vehicle_9_4_0_, vehiclemod0_.id as id1_4_0_, vehiclemod0_.id as id1_4_1_, vehiclemod0_.ccm as ccm2_4_1_, vehiclemod0_.engine_type_id as engine_t3_4_1_, vehiclemod0_.is_active as is_activ4_4_1_, vehiclemod0_.kw as kw5_4_1_, vehiclemod0_.manufacturer_id as manufact6_4_1_, vehiclemod0_.title as title7_4_1_, vehiclemod0_.vehicle_id as vehicle_9_4_1_, vehiclemod0_.year_of_manufacture as year_of_8_4_1_ from vehicle_model vehiclemod0_ where vehiclemod0_.vehicle_id=?
2019-06-23 02:04:11.046 WARN 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-06-23 02:04:11.046 ERROR 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'vehiclemod0_.vehicle_id' in 'field list'
2019-06-23 02:04:11.048 DEBUG 5896 --- [nio-8080-exec-1] org.hibernate.SQL : select vehiclemod0_.vehicle_id as vehicle_9_4_0_, vehiclemod0_.id as id1_4_0_, vehiclemod0_.id as id1_4_1_, vehiclemod0_.ccm as ccm2_4_1_, vehiclemod0_.engine_type_id as engine_t3_4_1_, vehiclemod0_.is_active as is_activ4_4_1_, vehiclemod0_.kw as kw5_4_1_, vehiclemod0_.manufacturer_id as manufact6_4_1_, vehiclemod0_.title as title7_4_1_, vehiclemod0_.vehicle_id as vehicle_9_4_1_, vehiclemod0_.year_of_manufacture as year_of_8_4_1_ from vehicle_model vehiclemod0_ where vehiclemod0_.vehicle_id=?
2019-06-23 02:04:11.049 WARN 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-06-23 02:04:11.049 ERROR 5896 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'vehiclemod0_.vehicle_id' in 'field list'

Found a solution here: https://www.baeldung.com/jpa-many-to-many
I've been looking it a bit wrong since this is a ManyToMany relationship. My idea was to go to the "middle" table and select all vehicle_id where userId = :id and from the list of vehicle_ids to get details for each vehicle.
Here is the correct implementation that works:
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String firstName;
private String lastName;
private boolean isActive;
#OneToMany(mappedBy = "user")
private Set<UserVehicle> userVehicles;
#Entity(name = "user_vehicle")
public class UserVehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private boolean isActive;
#ManyToOne
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#Entity(name = "vehicle_model")
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long manufacturerId;
private String title;
private int ccm;
private int kw;
private int yearOfManufacture;
private int engineTypeId;
private boolean isActive;
#OneToMany(mappedBy = "vehicle")
private Set<UserVehicle> userVehicles;
Finally, in the repository Interface I have a method like this:
public interface UserVehicleRepository extends CrudRepository<UserVehicle, Long> {
Iterable<UserVehicle> findVehicleModelsByUserId(Long userId);
The output comes out like this:
[
{
"id": 2,
"vehicle": {
"id": 2,
"manufacturerId": 1,
"title": "Fazer FZ1S",
"ccm": 998,
"kw": 110,
"yearOfManufacture": 2008,
"engineTypeId": 1,
"active": true
},
"user": {
"id": 2,
"username": "sfajkovic",
"password": "33",
"firstName": "Ivan",
"lastName": "Fajkovic",
"active": true
},
"active": true
}
]
The issue is not that I don't really want the user object in the result so now I need to figure that one out. And after that, I will try to map VehicleManufacturer class to get those results as well in the same response.

Here is the solution to do what I intentionally wanted - to search Vehicles based on the user_id in the user_vehicle table:
#Entity(name = "user_vehicle")
public class UserVehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private boolean isActive;
private long userId;
#ManyToOne
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
#Entity(name = "vehicle_model")
public class Vehicle {
#Id
#Min(1)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long manufacturerId;
private String title;
private int ccm;
private int kw;
private int yearOfManufacture;
private int engineTypeId;
private boolean isActive;
#OneToMany(mappedBy = "vehicle")
private Set<UserVehicle> userVehicles;
#Repository
public interface UserVehicleRepository extends CrudRepository<UserVehicle, Long> {
Iterable<UserVehicle> findVehicleModelsByUserId(Long userId);
The User class is actually not used at all:
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String firstName;
private String lastName;
private boolean isActive;
This is a OneToMany relationship now and is doing what I actually wanted in the first time.
Hope it helps someone

Related

Hibernate binding returns null on insert

I'm here to get help because I've been searching the internet for a few hours without finding my answers. Maybe someone here will have a solution to my problem.
I'm new with spring boot, so don't be too mean to me, I probably missed some information in the documentation.
I'm trying to add data to my database with a json file that looks like this one.
Everything works correctly up to the OneToMany relationship and vice versa ManyToOne (I followed the doc here).
I put Cascade.ALL to propagate to entities.
As you can see, Hibernate makes a binding with a null value, as if it had never been inserted whereas it seems to be at the previous step.
A lot of topics deal with this problem, and for many of them they have been solved with the null = false option, but it doesn't work for me.
Information:
A contact can have multiple email addresses, and an email address belongs to only one contact.
My code :
Emails
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "Email")
public class Email implements Serializable {
#Id
#GeneratedValue
#Column(name = "idEmail")
private int id;
#Column(name = "Email")
#JsonProperty("mail")
private String email;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Contact_idContact")
private Contact contact;
}
Contact
#Entity
#Table(name = "Contact")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
public class Contact implements Serializable {
#GeneratedValue
#Id
#Column(name = "idContact", nullable = false, unique = true)
private int id;
#Column(name = "Nom")
private String nom;
#Column(name = "Prenom")
private String prenom;
#Column(name = "Mobile")
private String mobile;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "contact")
private Set<Email> emails = new HashSet<>();
}
My json post :
{
"contacts": [
{
"nom": "myName",
"prenom": "MyFirstName",
"mobile": "06666666666",
"emails": [
{
"mail": "mail1#test.com"
},
{
"mail": "mail2#test.com"
}
],
}
]
}
POST Method
#PostMapping("/add")
public ResponseEntity<Void> addContact(#RequestBody Contact contact) {
Contact contact1 = contactService.save(contact);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(contact1.getId())
.toUri();
return ResponseEntity.created(location).build();
}
Error:
2020-10-15 16:40:16.501 DEBUG 13608 --- [nio-9000-exec-2] org.hibernate.SQL : insert into Contact (Mobile, Nom, Prenom, idContact) values (?, ?, ?, ?)
2020-10-15 16:40:16.502 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [06666666666]
2020-10-15 16:40:16.503 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [MyName]
2020-10-15 16:40:16.504 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [MyFirstName]
2020-10-15 16:40:16.505 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [INTEGER] - [46]
2020-10-15 16:40:16.510 DEBUG 13608 --- [nio-9000-exec-2] org.hibernate.SQL : insert into Email (Contact_idContact, Email, idEmail) values (?, ?, ?)
2020-10-15 16:40:16.511 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [null]
2020-10-15 16:40:16.512 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [mail1#test.com]
2020-10-15 16:40:16.512 TRACE 13608 --- [nio-9000-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [INTEGER] - [47]
2020-10-15 16:40:16.542 WARN 13608 --- [nio-9000-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1048, SQLState: 23000
2020-10-15 16:40:16.542 ERROR 13608 --- [nio-9000-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'Contact_idContact' cannot be null
2020-10-15 16:40:16.636 ERROR 13608 --- [nio-9000-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
In this case, Contact_idContact should be 46 as inserted.
What's wrong with it?
I resolved my problem. I forgot a CascadeType.ALL in Email.java.

Why Spring boot JPA native update is giving PSQLException with postgres Database?

I have this Entity class:
#Entity
#Table(name = "inbox_inbox")
#Getter
#Setter
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Inbox implements Serializable {
#Id
private int id;
#Column(name = "created")
private Date created;
#Column(name = "modified")
private Date modified;
#Column(name = "status")
private String status;
}
I have this repository:
#Repository
public interface InboxRepository extends JpaRepository<Inbox, Integer> {
List<Inbox> findInboxesByStatus(String status);
#Modifying
#Transactional
#Query(value = "update inbox_inbox i set i.status = ?2 where i.id = ?1", nativeQuery = true)
int setInboxStatusById(int id, String status);
}
If I call findInboxesByStatus(String status) with required status, then it gives the expected result. But when calling setInboxStatusById() then it is giving me an exception!
I am giving my calling part here:
int updatedRows = inboxRepository.setInboxStatusById(2, "processing");
And getting this exception:
2020-02-10 22:21:57.486 DEBUG 7 --- [main] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.setInboxStatusById]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2020-02-10 22:21:57.486 DEBUG 7 --- [main] o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(1663686815<open>)] for JPA transaction
2020-02-10 22:21:57.486 DEBUG 7 --- [main] o.h.e.t.internal.TransactionImpl : On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
2020-02-10 22:21:57.486 DEBUG 7 --- [main] o.h.e.t.internal.TransactionImpl : begin
2020-02-10 22:21:57.486 DEBUG 7 --- [main] org.postgresql.jdbc.PgConnection : setAutoCommit = false
2020-02-10 22:21:57.486 DEBUG 7 --- [main] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#10fc01e0]
2020-02-10 22:21:57.487 DEBUG 7 --- [main] org.hibernate.SQL : update inbox_inbox i set i.status = ? where i.id = ?
2020-02-10 22:21:57.487 DEBUG 7 --- [main] o.h.engine.jdbc.spi.SqlExceptionHelper : could not execute statement [n/a]
org.postgresql.util.PSQLException: ERROR: column "i" of relation "inbox_inbox" does not exist
Position: 26
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2497) ~[postgresql-42.2.8.jar!/:42.2.8]
at .....
Why getting this? I searched for some solution in this site as well. But it seems, it is a new thread. So asking for help. Thanks in advance.
Using aliases in the update query is not allowed. Please, use the following:
#Modifying
#Transactional
#Query(value = "update inbox_inbox set status = ?2 where id = ?1", nativeQuery = true)
int setInboxStatusById(int id, String status);

Logging SQL queries in spring, way more hidden SQL requests than expected

So i enabled sql logging in spring to see which sql queries are executed, because some requests took longer than expected.
application.yml:
logging:
level:
org:
hibernate:
SQL: DEBUG
type:
descriptor:
sql:
BasicBinder: TRACE
Then i did an API-request on the following API-method:
#GetMapping(params = "employeeId")
public ResponseEntity<List<DailyEntry>> getDailyEntriesFromEmployeeId(#RequestParam Long employeeId) {
return ResponseEntity.ok(dailyEntryService.getDailyEntriesFromEmployeeId(employeeId));
}
This calls this service method:
List<DailyEntry> getDailyEntriesFromEmployeeId(Long employeeId) {
List<DailyEntry> dailyEntries = dailyEntryRepository.findByEmployeeId(employeeId);
return dailyEntries;
}
findByEmployee() is defined in the CrudRepository:
List<DailyEntry> findByEmployeeId(#Param("id") long id);
The DailyEntry Entity looks like this:
#Data
#Entity
#JsonInclude(JsonInclude.Include.NON_NULL)
public class DailyEntry {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private LocalDate date;
private LocalTime startTime;
private LocalTime endTime;
// more unimportant fields
#ManyToOne
private Project project;
#ManyToOne
private Employee employee;
}
So by calling that API-method i expected to see one SQL method being executed, the one called by dailyEntryRepository.findByEmployeeId(employeeId);.
But on top of that, im actually seeing the following SQL request for each DailyEntry in the list:
2019-12-09 11:54:13.942 DEBUG 1400 --- [nio-8080-exec-4] org.hibernate.SQL : select project0_.id as id1_9_0_, project0_.archived_date as archived2_9_0_, project0_.budget as budget3_9_0_, project0_.creation_date as creation4_9_0_, project0_.customer_id as customer8_9_0_, project0_.default_daily_entry_settings_id as default_9_9_0_, project0_.description as descript5_9_0_, project0_.is_archived as is_archi6_9_0_, project0_.name as name7_9_0_, customer1_.id as id1_0_1_, customer1_.address as address2_0_1_, customer1_.name as name3_0_1_, customer1_.smallest_time_unit as smallest4_0_1_, customer1_.smallest_time_unit_enabled as smallest5_0_1_, customer1_.supplier_number as supplier6_0_1_, defaultdai2_.id as id1_2_2_, defaultdai2_.end_date_hour as end_date2_2_2_, defaultdai2_.end_date_minute as end_date3_2_2_, defaultdai2_.pause as pause4_2_2_, defaultdai2_.performance_record as performa5_2_2_, defaultdai2_.start_date_hour as start_da6_2_2_, defaultdai2_.start_date_minute as start_da7_2_2_ from project project0_ inner join customer customer1_ on project0_.customer_id=customer1_.id left outer join default_daily_entry_settings defaultdai2_ on project0_.default_daily_entry_settings_id=defaultdai2_.id where project0_.id=?
2019-12-09 11:54:13.942 TRACE 1400 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [8]
And after that, theres this request for each DailyEntry in the list:
2019-12-09 11:57:18.142 DEBUG 1400 --- [nio-8080-exec-8] org.hibernate.SQL : select employees0_.project_id as project_1_4_0_, employees0_.employee_id as employee2_4_0_, employee1_.id as id1_3_1_, employee1_.address as address2_3_1_, employee1_.email as email3_3_1_, employee1_.enabled as enabled4_3_1_, employee1_.first_name as first_na5_3_1_, employee1_.last_name as last_nam6_3_1_, employee1_.weekly_hours as weekly_h7_3_1_, employee1_.weekly_hours_enabled as weekly_h8_3_1_ from employee_projects employees0_ inner join employee employee1_ on employees0_.employee_id=employee1_.id where employees0_.project_id=?
2019-12-09 11:57:18.142 TRACE 1400 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [8]
What actually happens here and why? He gets the id of the project by finding it with... the id he already has?
Thats the Project Entity if that matters:
#Data
#Entity
#ToString(exclude = {"employees"})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(unique = true)
private String name;
private Integer budget;
private String description;
private Boolean isArchived;
private LocalDate archivedDate;
private LocalDate creationDate;
#NotNull
#ManyToOne
private Customer customer;
#OneToOne(cascade = CascadeType.ALL)
private DefaultDailyEntrySettings defaultDailyEntrySettings;
#ManyToMany
#JoinTable(
name = "employee_projects",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "employee_id")
)
private List<Employee> employees;
#Transient
private Long usedBudget;
#Transient
private Integer notificationCount;
#Transient
private Boolean isInactive;
#Transient
private Integer inactiveSince;
}

JPA update record is failing due to #OneToMany

I have a very small doubt regarding #OneToMany mapping.
I have a model student and another model attendance.
A student can have multiple attendance. but student model should only be able to retrieve the attendance info.
But when I am trying to change some student info I am getting below error as it is trying to update attendance record.
here is my mapping
#Entity
#Table(name="student_detail")
#Getter #Setter
public class StudentDetailsModel {
#Id
#Column(name="reg_no",updatable = false, nullable = false)
private String regNo;
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
#JoinColumn(name = "reg_no")
private List<AttendanceModel> attendances;
}
and the exception I a getting.
update
student_detail
set
address=?,
alt_contact_number=?,
blood_group=?,
contact_number=?,
dob=?,
father_name=?,
first_name=?,
gender=?,
last_name=?,
middle_name=?,
mother_name=?,
photo_url=?,
school_id=?
where
reg_no=?
Hibernate:
update
attendance
set
reg_no=null
where
reg_no=?
2019-01-13 12:12:52.922 WARN 10708 --- [nio-8081-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23502
2019-01-13 12:12:52.923 ERROR 10708 --- [nio-8081-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: null value in column "reg_no" violates not-null constraint
Detail: Failing row contains (null, 1, 2019-01-05, t, 2).
2019-01-13 12:12:52.926 INFO 10708 --- [nio-8081-exec-1] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [reg_no]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
my attenance model is as follows
#Entity
#Table(name="attendance")
#Getter #Setter
public class AttendanceModel {
//#EmbeddedId
//private AttendanceId attendanceId;
#Id
#Column(name="attendance_id")
private long id;
#Column(name="reg_no")
private String regNo;
#Column(name="subject_id")
private long subjectId;
#Column(name="class_date")
private Date classDate;
#Column(name="present")
private boolean present;
}
Could you show me Student Model.If i look your code post : You using Unidirectional relationship.
I think it must :
#OneToMany(fetch = FetchType.LAZY , cascade = CascedeType.ALL)
#JoinColumn(name="attendance_id")
private List<AttendanceModel> attendances = new ArrayList<>();

Hibernate. Many to many giving an "org.hibernate.PersistentObjectException detached entity passed to persist" error

I am new using Spring Boot and Hibernate and I am working on a project that has a simple Many to Many association. A sport Center has many services and viceversa. When i try to enter a json like this to create a new sport center and associate some existing service:
{
"name" : "SC1",
"address" : "Street 1111",
"description" : "A Sport center",
"email" : "sc#sc.com",
"phones" : [{"number" : "123456"}, {"number" : "654321"}],
"services" : [{"idService" : 1}, {"idService" : 2}],
"commune" : {"idCommune" : 1},
"users" : [{"idUser":62}]
}
I am receiving this error:
2018-06-25 03:56:32.670 DEBUG 20696 --- [nio-8080-exec-6] org.hibernate.SQL : insert into sport_center (address, Commune_idCommune, description, email, name) values (?, ?, ?, ?, ?)
2018-06-25 03:56:32.671 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Street 1111]
2018-06-25 03:56:32.671 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [1]
2018-06-25 03:56:32.671 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [A Sport center]
2018-06-25 03:56:32.671 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [VARCHAR] - [sc#sc.com]
2018-06-25 03:56:32.671 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [VARCHAR] - [SC1]
2018-06-25 03:56:32.800 DEBUG 20696 --- [nio-8080-exec-6] org.hibernate.SQL : insert into phone (number, Sport_center_idSport_Center) values (?, ?)
2018-06-25 03:56:32.800 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [123456]
2018-06-25 03:56:32.800 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [252]
2018-06-25 03:56:32.942 DEBUG 20696 --- [nio-8080-exec-6] org.hibernate.SQL : insert into phone (number, Sport_center_idSport_Center) values (?, ?)
2018-06-25 03:56:32.942 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [654321]
2018-06-25 03:56:32.942 TRACE 20696 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [252]
2018-06-25 03:56:33.368 ERROR 20696 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.eiffel.canchai.model.Service; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.eiffel.canchai.model.Service] with root cause
org.hibernate.PersistentObjectException: detached entity passed to persist: com.eiffel.canchai.model.Service
It is important to mention that I have already added some rows in the service table. So, the idea is to add a Sport Center and associate these services to it.
Could you please help me with this?
My classes below:
SportCenter.java
#Entity
#Table(name = "sport_center")
public class SportCenter implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idSport_Center")
private Integer idSportCenter;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#Column(name = "description")
private String description;
#Column(name = "email")
private String email;
#JoinTable(name = "sport_center_has_user", joinColumns = {
#JoinColumn(name = "Sport_center_idSport_Center", referencedColumnName = "idSport_Center")}, inverseJoinColumns = {
#JoinColumn(name = "User_idUser", referencedColumnName = "idUser")})
#ManyToMany(cascade = CascadeType.ALL)
private List<User> users;
#JoinTable(name = "sport_center_has_service", joinColumns = {
#JoinColumn(name = "Sport_center_idSport_Center", referencedColumnName = "idSport_Center")}, inverseJoinColumns = {
#JoinColumn(name = "Service_idService", referencedColumnName = "idService")})
#ManyToMany(cascade = CascadeType.ALL)
private List<Service> services;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "sportCenter")
#JsonIgnore
private List<ImageField> imageFields;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "sportCenter")
#JsonIgnore
private List<Field> fields;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "sportCenter", fetch = FetchType.LAZY)
private List<Phone> phones;
#JoinColumn(name = "Commune_idCommune", referencedColumnName = "idCommune")
#ManyToOne(optional = false)
private Commune commune;
public SportCenter() {
}
Service.java
#Entity
#Table(name = "service")
public class Service implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idService")
private Integer idService;
#Column(name = "name")
private String name;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "services", cascade = CascadeType.ALL)
#JsonIgnore
private List<SportCenter> sportCenters;
public Service() {
}
ServiceDao.java
#Repository
#Transactional
public class ServiceDao implements IServiceDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public void save(Service entity) {
entityManager.persist(entity);
}
The SportCenterDao is almost the same that the above :)
Controller
#Controller
#RequestMapping("/sportcenter")
#CrossOrigin
public class SportCenterController {
#Autowired
private ISportCenterService sportCenterService;
#PostMapping("/register")
public ResponseEntity<?> registerSportCenter(#RequestBody SportCenter sc){
if (sc.getEmail().length() == 0 || sc.getName().length() == 0 || sc.getAddress().length() == 0 || sc.getPhones().size() == 0 || sc.getServices().size() == 0) {
return new ResponseEntity(new ErrorMsg("Check parameters. One of them is missing"),HttpStatus.NO_CONTENT);
}
for (Phone p : sc.getPhones()) {
p.setSportCenter(sc);
}
for (Service s : sc.getServices()) {
s.addSportCenters(sc);
}
sportCenterService.save(sc);
return new ResponseEntity(HttpStatus.OK);
}
I think that probably is an error mapping the associations but I am not sure.
Also, if you see that there is a way to improve my code, please let me know :).. Advices are always good.
I am using Spring Boot V2.0.3 and Hibernate 5.3
Thanks in advance!
The problem here is that you are cascading the persist event from parent to child.
When the child entity has a non-zero ID, then hibernate will assume it already exists.
Since you are getting the data from the rest service, I guess the child "Service" entities are not detached. Hence the error "Detached entity passed to persist".
You need to persist the parent first, make the children managed via merge call, then link the parent to the child entities, or get rid of the Cascade.All.

Categories

Resources