Hibernate binding returns null on insert - java

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.

Related

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);

Unable to create a JOIN using Spring Data JPA

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

JPA/Hibernate: ManyToMany delete relation on Join Table

Disclaimer: This is my first Java project; learning as I go.
Background: I've inherited a legacy database on which to build a new RESTful API. We're using Elide with Spring Boot to provide a JSON API compliant service.
Reference: Example source code
Problem: We have entities with a many-to-many relationship to each other and themselves by way of a join table. Consider the followig schema:
CREATE TABLE ALPHA (
ID VARCHAR(255),
NAME VARCHAR(255),
CONSTRAINT PK_ALPHA PRIMARY KEY (ID)
);
CREATE TABLE BRAVO (
ID VARCHAR(255),
NAME VARCHAR(255),
CONSTRAINT PK_BRAVO PRIMARY KEY (ID)
);
CREATE TABLE RELATIONSHIP (
ID INT AUTO_INCREMENT,
FROM_ID VARCHAR(255),
TO_ID VARCHAR(255)
);
Where the resource entities are modeled as follows:
public class Alpha implements Serializable {
private String id;
private String name;
private Set<Alpha> alphas = new HashSet<>();
private Set<Bravo> bravos = new HashSet<>();
#Id
#Column(name = "ID", unique = true, nullable = false)
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
public String getId() {
return id;
}
public String getName() {
return name;
}
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(
name = "RELATIONSHIP",
joinColumns = #JoinColumn(name = "FROM_ID"),
inverseJoinColumns = #JoinColumn(name = "TO_ID")
)
public Set<Alpha> getAlphas() {
return alphas;
}
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(
name = "RELATIONSHIP",
joinColumns = #JoinColumn(name = "FROM_ID"),
inverseJoinColumns = #JoinColumn(name = "TO_ID")
)
public Set<Bravo> getBravos() {
return bravos;
}
}
And the relationship table:
public class Relationship implements Serializable {
private Integer id;
private String fromId;
private String toId;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
#Column(name = "FROM_ID")
public String getFromId() {
return fromId;
}
#Column(name = "TO_ID")
public String getToId() {
return toId;
}
}
Now let's say we have an Alpha record A1 with relationships to A2, A3, B1, and B2. First we delete the relationship to A2.
From our API this would be a DELETE request to http://localhost:9000/api/alphas/a1/relationships/alphas with BODY
{
"data": [
{
"type": "alphas",
"id": "a2"
}
]
}
Behind the scenes Hibernates does what I'm expecting and generates the following SQL queries:
2018-07-13 09:48:23.687 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL :
Hibernate:
select
alpha0_.id as id1_0_,
alpha0_.name as name2_0_
from
alpha alpha0_
where
alpha0_.id in (
?
)
2018-07-13 09:48:23.688 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [a1]
2018-07-13 09:48:23.690 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL :
Hibernate:
select
alphas0_.from_id as from_id2_2_0_,
alphas0_.to_id as to_id3_2_0_,
alpha1_.id as id1_0_1_,
alpha1_.name as name2_0_1_
from
relationship alphas0_
inner join
alpha alpha1_
on alphas0_.to_id=alpha1_.id
where
alphas0_.from_id=?
2018-07-13 09:48:23.690 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [a1]
2018-07-13 09:48:23.699 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL :
Hibernate:
select
alpha0_.id as id1_0_,
alpha0_.name as name2_0_
from
alpha alpha0_
where
alpha0_.id in (
?
)
2018-07-13 09:48:23.699 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [a2]
2018-07-13 09:48:23.721 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL :
Hibernate:
delete
from
relationship
where
from_id=?
and to_id=?
2018-07-13 09:48:23.722 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [a1]
2018-07-13 09:48:23.724 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [a2]
The key piece being delete from relationship where from_id=? and to_id=?
Now the problem arises when trying to delete the second Alpha relationship A3, in which Hibernate does almost the exact same sequence, except for the DELETE query which omits the and to_id=? from the query, i.e.
Hibernate:
delete
from
relationship
where
from_id=?
Which has the unintended consequence of deleting all other A1 relationships in the table, i.e. B1 and B2.
So that is the crux of my problem. It seems like Hibernate is only seeing one other related Alpha record and therefore deciding to simplify the query by omitting the and to_id statement.
I'm probably missing something terribly obvious!
I should also point out that I attempted to use a composite key on the relationship table but to no avail.
This is an unusual design, which I suspect is confusing Hibernate. Sharing a single join table between multiple Many-to-many relationships, isn't good database design, for one it can't have any foreign keys/referential integrity.
Secondly, Hibernate manages relationships, and therefore has control over the #JoinTable, I don't know how it would handle multiple entity relationships mapped with the same table. Evidently, not very well though!
The simplest solution (if you're able to), would be to have 2 mapping tables. One for the relationship between Alpha-Alpha and another between Alpha-Bravo.

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.

Does Spring Data JPA ignore #Fetch annotations?

I have the following JPA Mapping (getters and setters out for brevity purposes, the DDL also gets generated from the code which may/may not play a role):
Expense
#Entity
public class Expense {
#Id
#GeneratedValue
private Long id;
private String name;
private Long amount;
private Boolean monthly;
#OneToOne
#JoinColumn(name = "category")
#Fetch(FetchMode.JOIN)
private Category category;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<Label> labels = new ArrayList<>();
//constructor, getters and setters...
}
Category
#Entity
public class Category {
#Id
private String name;
//constructor, getters and setters...
}
Label
#Entity
public class Label {
#Id
private String name;
//constructor, getters and setters...
}
Usage with JpaRepository
So I am using a JpaRepository that looks like this:
public interface ExpensesRepository extends JpaRepository<Expense, Long> {
#Query("SELECT e FROM Expense e LEFT JOIN FETCH e.category")
List<Expense> findAllExpensesExploded();
}
When I use the default findAll() method of the JpaRepository, I get a n+1 select problem:
2017-01-03 19:35:22.665 DEBUG 26040 --- [nio-8080-exec-1] org.hibernate.SQL : select expense0_.id as id1_1_, expense0_.amount as amount2_1_, expense0_.category_name as category5_1_, expense0_.monthly as monthly3_1_, expense0_.name as name4_1_ from expense expense0_
2017-01-03 19:35:22.673 DEBUG 26040 --- [nio-8080-exec-1] org.hibernate.SQL : select category0_.name as name1_0_0_ from category category0_ where category0_.name=?
2017-01-03 19:35:22.674 TRACE 26040 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Rent]
2017-01-03 19:35:22.682 DEBUG 26040 --- [nio-8080-exec-1] org.hibernate.SQL : select category0_.name as name1_0_0_ from category category0_ where category0_.name=?
2017-01-03 19:35:22.683 TRACE 26040 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Insurance]
However, when I use my own findAllExpensesExploded() method I get a single SQL query:
2017-01-03 19:35:22.691 DEBUG 26040 --- [nio-8080-exec-1] org.hibernate.SQL : select expense0_.id as id1_1_0_, category1_.name as name1_0_1_, expense0_.amount as amount2_1_0_, expense0_.category_name as category5_1_0_, expense0_.monthly as monthly3_1_0_, expense0_.name as name4_1_0_ from expense expense0_ left outer join category category1_ on expense0_.category_name=category1_.name
My expectation was for both findAll() and findAllExpensesExploded() to be executed with a single SQL query.
My query worked because I seemed to have constructed it correctly
But why does the findAll() not work with the given mapping annotations? Is it possible that Spring Data is ignoring the #Fetch annotation?
An additional question that seems reasonable to ask is whether the default findAll() should only be used for simple entities? (where simple is defined as no associations).
The Default fetch mode is lazy. It's always a good practice to use #NamedEntityGraph and #EntityGraph annotations when working with Spring Data JPA.You can go through this
whether the default findAll() should only be used for simple entities? (where simple is defined as no associations).
Fetch Mode - LAZY will only fire for primary table. If in the code you call any other method that has a parent table dependency then it will fire Fetch Mode - SELECT.

Categories

Resources