Failed jpa query : Cannot call sendError() after the response has been committed - spring-boot

I want to get rooms by HospitalId, there relation between classes like it looks below:
#Entity(name = "rooms")
#Table( name = "rooms",
uniqueConstraints = {
#UniqueConstraint(columnNames = "roomNumber")
})
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long idRoom;
private String roomNumber;
#ManyToOne
(fetch = FetchType.EAGER)
#JoinColumn(nullable = false, name = "idhospital")
private Hospital hospital;
this is the query i used :
#Query("select s from rooms s where s.hospital.idHospital =:hospital")
List<Room> findRoomsByHospital( #Param("hospital") Long hospital);
Error in the back :
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472) ~[tomcat-embed-core-9.0.24.jar:9.0.24]
Is send data but wrong form to the frontend :
error
I really need help can't understand how to deal with it.

I fount the problem :The repitation of data
Solution in to add this notation #JsonIgnore like this :
#ManyToOne
(fetch = FetchType.EAGER)
#JoinColumn(nullable = false, name = "idhospital")
#JsonIgnore
private Hospital hospital;
This is used for to restrict the data to repeat itself

I think you are query is not correct. You have used rooms there without using nativeQuery=true.
you can try below in RoomRepository
#Query("select s from Room s where s.hospital.idHospital =:hospital")
List<Room> findRoomsByHospital( #Param("hospital") Long hospital);
changed rooms to Room
Alternatively, you can try query by a method as shown below, you don't need to use the #Query in this case.
If you are passing hospitalId long use below
List<Room> findRoomsByHospitalIdHospital ( #Param("idHospital") Long hospital);
If you are passing hospital Object use below
List<Room> findRoomsByHospital ( #Param("Hospital ") Hospital hospital);

Related

Spring MapsId not resolving target entity

I have such a case where I need to have internally many-to-one using hibernate proxies and only id externally, here using MapsId. The issue appears when I try to save something, because the target entity is not fetched, when I set the value only on the id.
Let's take an example: I have an Account table and DeviceConfig table. Inside the DeviceConfig's class definition, I add account in a many-to-one relation and accountId in relation with #MapsId.
Now when creating, I always set a value to accountId, but never the value is picked up, and the backend throws an SQL error, because the field cannot be null.
#Table(name = "djl_device_config")
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
public class DeviceConfig extends CoreEntity {
...
#JsonIgnore
#ManyToOne
#MapsId("accountId")
#JoinColumn(name = "account_id")
private Account account;
#Column(name = "account_id", insertable = false, updatable = true, nullable = true)
private UUID accountId;
}
So I suppose this is a config error on my side, but I've been reading the JPA for these three days and I still don't know what's wrong or what I should do to achieve the behaviour I expect.
That for any help you'll provide.

Put Reference from Audit table to Another Table in Hibernate Envers

I'm using Hibernate Envers for Auditing Change Data, I have a Class that store information about companies like this :
#Getter
#Setter
#Entity
#Table(name = "COMPNAY")
#Audited
public class Compnay {
private String name;
private String code;
}
and it's using Envers for keeping the changes of companies.
also, I have a class for Keep the data of items that manufacture in any of this company, the class will be like this :
#Getter
#Setter
#Entity
#Table(name = "COMPNAY")
#Audited
public class Item {
#Column(name = "NAME", nullable = false)
private String name ;
#Column(name = "CODE", nullable = false)
private String code;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COMPANY_ID", nullable = false)
private Compnay compnay;
}
Consider that there is a company in company table like this :
ID
NAME
CODE
1
Apple
100
2
IBM
200
and the data in the item's table will be like this :
ID
NAME
CODE
COMPANY_ID
3
iPhone
300
1
4
iPad
400
1
if I edit the information of Apple company and change the code from 100 to 300 how can I fetch the information of Items that were saved before this change with the previous code? Is there is any way to reference to audit table?
Yes, you can write a HQL query that refers to the audited entities. Usually, the audited entities are named like the original ones, with the suffix _AUD i.e. you could write a query similar to the following:
select c, i
from Company_AUD c
left join Item_AUD i on i.id.revision < c.id.revision
where c.originalId = :companyId

"could not initialize proxy - no Session" For Multiple ManyToMany relationships in the parent

I have a Parent User Class that has multiple ManyToMany Relationships.
#Table(name = "user")
public class User {
..
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "user_address",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "address_id")}
)
#JsonIgnore
private final List<Address> addresses = new ArrayList<Address>();
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "reports",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "reports_id")}
)
#JsonIgnore
private final List<Reports> reports = new ArrayList<Reports>();
}
When I access the FIRST ManyToMany property, everything works fine. However, immediately after
accessing the first, when I try to access the SECOND ManyToMany Property I get the "could not initialize proxy - no Session" exception:
#Component
public class Combiner {
public void combineData() {
...
List<Address> addresses = user.getAddress(); // This works
List<Reports> reports = user.getReports(); // Get the error here
..
}
}
The Address and Reports classes have the inverse relationship as many ManyToMany back to the User Entity Above.
public class Address {
#ManyToMany(mappedBy = "addresses", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
public class Reports {
#ManyToMany(mappedBy = "reports", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
I tried searching SO for the same error where there are MULTIPLE relationships like mine and the first passes but second fails, but could'nt find a post (or google couldn't understand the search terms, if anyone knows a pre-existing one - please let me know).
Could someone assess what else Im missing?
I've tried these so far to no avail:
Added #Transactional to the parent Service class that calls Combiner above
Made the second failing relationship EAGER. (as i understand it you cant make BOTH EAGER since i get a multiple bags error probably because of Cartesian join)
AM Using SpringBoot (2.2.4) with Hibernate Core {5.4.10.Final}
Approach one:
Make #ManyToMany uni-directional. The exception clearly says it can not initialize the collection of role you have in User class.
As you asked in the comment section Why can't this use case be Bi Directional - You can make this bi-directional as well.
Approach two: make collection of role EAGER or use Hibernate.initialize() to initialize the collection.
Bonus: you can make both collection EAGER by using Set not List.

Spring Boot many to many post method not updating data

My User class looks like this :
#Data
#Entity
public class User {
#Id
Long userID;
#ManyToMany(mappedBy = "admins")
private List<ClassRoom> classRooms = new ArrayList<>();
}
And my ClassRoom class like this :
#Data
#Entity
public class ClassRoom {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long classRoomID;
#ManyToMany
#JoinTable(name ="classroom_user",
joinColumns = #JoinColumn(name = "classroom_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> admins = new ArrayList<>();
}
And in my UserController class, I have :
#PostMapping("user/{id}/c")
User addClassRoom(#PathVariable Long id,#RequestBody ClassRoom newClassRoom)
{
logger.debug(repository.findById(id));
return repository.findById(id)
.map(user -> {
user.getClassRooms().add(newClassRoom);
user.setClassRooms(user.getClassRooms());
return repository.save(user);
})
.orElseGet(() -> {
return null;
});
}
And I POST and empty JSON ({}) and I see no change in my users. The Classroom or an empty Classroom doesn't get added in the User.
What is the problem here? How can I resolve this ?
user.getClassRooms().add(newClassRoom); is suffice, user.setClassRooms(user.getClassRooms()); not required.
You will have to perform cascade save operation.List all cascade types explicitly and don't use mappedBy, instead use joincolumns annotation.
Can you paste the logs, please? Is Hibernate doing any insert into your table? Has the database schema been created in the DB correctly? One thing I recommend you to do is to add a custom table name on the top of your User class, using annotations like so: #Table(name = "users"). In most SQL dialects user is a reserved keyword, hence it is recommended to always annotate User class a bit differently, so that Hibernate won't have any problems to create a table for that entity.
IMO you must find classRoom by its id from repository, if it's new, you must create a new entity and save it first. Then assign it to user and save it.
The object you receive from the post method was not created by the entity manager.
After using user.getClassRooms().add(newClassRoom);
We must use userRepository.save(user);

JPA Bi-directional OneToMany does not give me the desired collection

Im trying to map a oracle db model with JPA entities (Using eclipselink) - i have following simple setup :
table program with primary key id
table multi_kupon with compound primary keys id, spil and foreign key program_id
when i try to fetch program with a simple select i would expect the go get a list
of multi_kupon's but im getting a list of size 0. I've made certain that when i do
a select with joins i get data from both program and multi_kupon.
I believe that its got to do with my relations of the 2 entities - hope somebody can point me to my mistake(s)
Snippet from Entity 'Program' :
#Entity
#Table(name = "PROGRAM", schema = "", catalog = "")
public class Program implements Serializable{
#Id
private Integer id;
private List<MultiKupon> MultiKuponList;
#OneToMany(mappedBy = "program", fetch = FetchType.EAGER)
#JoinColumns({#JoinColumn(name = "id", referencedColumnName = "id"),
#JoinColumn(name = "spil", referencedColumnName = "spil")})
public List<MultiKupon> getMultiKuponList() {
return multiKuponList;
}
Snippet from Entity 'MultiKupon' :
#Entity
#Table(name = "MULTI_KUPON", schema = "", catalog = "")
public class MultiKupon implements Serializable {
private Integer id;
private String spil;
private Program program;
#ManyToOne
public Program getProgram() {
return program;
}
My stateless bean :
#Stateless
public class PostSessionBean {
public Program getProgramById(int programId) {
String programById = "select p from Program p where p.id = :id";
Program program = null;
try {
Query query = em.createQuery(programById);
query.setParameter("id", programId);
program = (Program) query.getSingleResult();
I do get the correcct program entity with data, but the list with
multi_kupon data is size 0
What im i doing wrong here ??
The mapping is incorrect, as you have specified both that the OneToMany is mappedBy = "program" and that it should use join columns. Only one or the other should be used, and since there is a 'program' mapping, I suggest you use:
#OneToMany(mappedBy = "program", fetch = FetchType.EAGER)
public List getMultiKuponList()
And then define the join columns on MultiKupon's ManyToOne mapping to define which fields should be used for the foreign keys to the Program table.
Since you have made this a bidirectional relationship, you must also maintain both sides of your relationship. This means that every time you add a MultiKupon and want it associated to a Program you must both have it reference the Program, AND add the instance to the Program's collection. If you do not, you will run into this situation, where the cached Program is out of sync with what is in the database.
It is much cheaper generally to keep both sides in sync, but if this is not an option, you can correct the issue (or just verify that this is the situation) by calling em.refresh(program). If the mappings are setup correctly, the instance and its list will be repopulated with what is in the database.
According to http://sysout.be/2011/03/09/why-you-should-never-use-getsingleresult-in-jpa/
Try to retrieve first program object of List:
List<Program> pList = query.getResultList();
if(!pList.isEmpty()){
return pList.get(0);
}

Resources