I have a problem with a Many to Many relation.
Here I'll paste a code
User.kt
#Entity
#Getter
#Setter
#Builder
#Inheritance(strategy = InheritanceType.JOINED)
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "USER", schema = "public")
data class User(
#JsonView(Views.Base::class)
var username: String,
#JsonView(Views.UserExtended::class)
#OneToMany(mappedBy = "user", cascade = [CascadeType.ALL], orphanRemoval = true)
#JsonManagedReference
var projects: List<UserProject>? = mutableListOf()
UserProject.kt
#Entity
#Getter
#Setter
#Builder
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "USER_PROJECT", schema = "public")
data class UserProject(
#JsonView(Views.ProjectExtended::class)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
val user: User?,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "project_id")
val project: Project?,
#JsonView(Views.ProjectExtended::class)
val projectRole: String
) : BaseEntity() {}
Project.kt
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "PROJECT", schema = "public")
data class Project (
#JsonView(Views.Base::class)
val name: String?,
#OneToMany(mappedBy = "project", cascade = [CascadeType.ALL], orphanRemoval = true)
#JsonView(Views.ProjectExtended::class)
var users: List<UserProject>?
) : BaseEntity() {}
And i'm Going throught infinity loop.
What I've done is simply doing a Views to overcome a problem. But I'm also using QueryDSL on #RepositoryRestResource which give me auto predicate on url's
And the problem is, I cannot simply add a #Jsonview To RepositoryRestResource interface.
I've tried doing a Projection but it gave me the result of Infinity loop cuz the #JsonView doesnt work on Projection and interface
Does someone have a suggestion how could I overcome this problem?
Related
Stack Java 11 , spring boot 2.7, jpa
I try to creat relationship OneToOne Nullable and with no update or insert cascade.
When i call saveEntity i want it to save only the id and only check if the id exists.
the tables are in different databases
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "USER_ROLE", uniqueConstraints = {#UniqueConstraint(columnNames = {"USR_ID"})})
#EntityListeners(AuditingEntityListener.class)
#Builder
public class UserRoleEntity {
#Id
#Column(name = "USR_ID")
private String id;
#OneToOne(fetch = FetchType.EAGER, targetEntity = UserAEntity.class, cascade = {
CascadeType.DETACH, CascadeType.MERGE
})
#JoinColumn(name = "USR_ID", referencedColumnName = "USA_ID", nullable = true, insertable = false, updatable = false)
private UserAEntity userAEntity;
...
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_USER_ATTRIBUTE", uniqueConstraints = {#UniqueConstraint(columnNames = {"USA_ID"})})
#EntityListeners(AuditingEntityListener.class)
#Builder
public class UserAEntity {
#Id
#Column(name = "USA_ID")
private String id;
...
}
[one Save UserRoleEntity ]
If remove (fetch = FetchType.EAGER, targetEntity = UserAEntity.class, cascade = { CascadeType.DETACH, CascadeType.MERGE })
i have null error on creation but works with Update.
If i leave it works but the userAEntity changes are saved in the database
In my spring application , Lazy fetch is not working which is making fetch operation from db heavy. So,
when I fetch students using getStudentByRollNo() It is fetching fields EAGERLY which are even marked as Lazy. but when I am using findBySimpleNaturalId() it is loading lazy.
My classes are
Student.class
#Entity
#Builder
#ToString
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE)
class Student{
#Id
Long id;
#NaturalId
String rollNo;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "tutor_fk", nullable = false)
Tutor tutor;
#OneToMany(fetch = FetchType.LAZY, mappedBy = Courses.student, cascade = CascadeType.ALL)
Set<Courses> courses = new HashSet<>();
}
Courses.class
#Entity
#Getter
#Setter
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE)
class Courses{
Long id;
String create_date_time;
#ManyToOne
#JoinColumn(name = "student_fk")
Student student;
}
Tutor.class
#Entity
#Getter
#Setter
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE)
class Tutor{
Long id;
String name;
}
studentService.class
#Transactional
class studentService{
Students getStudent(String rollNo ){
return studentRepository.findOneByRollNO(rollNo);
} }
when I run my code on org.hibernate.SQL: DEBUG.
querys hit are
select course0_.id as id1_43_0_, course0_.create_date_time as create_date_time2_43_0_
from course course0_ where course0_.id=?
select tutor0_.id as id1_43_0_, tutor0_.name as name2_43_0_
from tutor tutor0_ where tutor0_.id=?
it is hitting above query as eager when these fields are marked LAZY
But when I am running getByNaturalId it is fetching lazy
#Transactional
class studentService{
Students getStudent(String rollNo){
Map<String, Object> filter = new HashMap<>();
filter.put("rollNo",rollNo);
return studentRepository.findBySimpleNaturalId(rollNo);
} }
I want to know why is it fetching eager for getByRollNo() and fetching lazy for findBySimpleNaturalId() ?
My database contains a table with a composite-primary-key such that one of the keys is a foreign key, and the other is supposed to be used to get an entity from an external service. The code looks somewhat like this:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Embeddable
#EqualsAndHashCode
public class PrimaryKey implements Serializable {
#Column(name = "A_ID")
private Long aId;
#Column(name = "F_ID")
private Long fId;
}
#Entity
#Table(name = "JOIN_ENTITY")
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class JoinEntity {
#EmbeddedId
private PrimaryKey pk;
#MapsId("aId")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "A_ID")
private EntityA a;
public getFId() { return pk.getFId(); }
}
#Entity
#Table(name = "ENTITY_A")
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class EntityA implements Serializable {
....
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true)
List<JoinEntity> list = new ArrayList<>();
}
When I get a JoinEntity saved and try to get EntityA from the database the list is not being populated, but if I get some JoinEntity from the database the related EntityA is recovered correctly. What do I do to get the JoinEntity list to be recovered with the EntityA?
You need to use FetchType.EAGER on the #OneToMany association in the EntityA class:
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
List<JoinEntity> list = new ArrayList<>();
This way when you retrieve a EntityA from the database, its JoinEntitys will be automatically retrieved.
Solved the problem adding an ENTITY_F table with the id and switching to a simple ManyToMany relationship.
I am trying to save entities with relationships. I annotated the attributes but hibernate doesn´t use the parent key of the parent entity, it's always 0.
My first entity is a TrafficJam that looks like this:
#Entity
#Table(name = "traffic_jam", schema = "public", catalog = "dwh")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class TrafficJamEntity {
#Id
#Column(name = "traffic_jam_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="traffic_jam_seq")
#SequenceGenerator(
name="traffic_jam_seq",
sequenceName="traffic_jam_id_sequence",
allocationSize=1
)
private long trafficJamId;
#Basic
#Column(name = "reading_id")
private long reading_id;
#OneToMany(mappedBy = "trafficJamEntity", cascade = CascadeType.PERSIST)
#Builder.Default
private Set<DisturbanceCourseEntity> disturbanceCourseEntitySet = new LinkedHashSet<>();
}
The child entity DisturbanceCourse looks like this:
#Entity
#Table(name = "disturbance_course", schema = "public", catalog = "dwh")
#IdClass(DisturbanceCourseEntityPK.class)
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class DisturbanceCourseEntity {
#Id
#Column(name = "traffic_jam_id")
private long trafficJamId;
#ManyToOne
#JoinColumn(name = "traffic_jam_id", nullable = false, insertable = false, updatable =
false)
private TrafficJamEntity trafficJamEntity;
}
If I add a disturbance course object to a traffic jam like this:
trafficJamEntity.getDisturbanceCourseEntitySet().add(DisturbanceCourseEntity
.builder()
.latitude(latlong[0])
.longitude(latlong[1])
.orderNumber(shapeId)
.build());
And try to save I get this error:
Fail to write to Database because: A different object with the same identifier value was
already associated with the session
[space.rocit.trafficetl.entities.DisturbanceCourseEntity#DisturbanceCourseEntityPK(trafficJamId=0, orderNumber=1)]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [space.rocit.trafficetl.entities.DisturbanceCourseEntity#DisturbanceCourseEntityPK(trafficJamId=0, orderNumber=1)]
I'm thinking that my definition of the relationship is wrong, any suggestions?
Edit.:
Here is my DisturbanceCourseEntityPK Class:
#Data
public class DisturbanceCourseEntityPK implements Serializable {
private long trafficJamId;
private int orderNumber;
}
Edit 2.:
Yesterday i gave it another try and found out that the generated keys are set correctly when trying to save. The problem is in the setting of foreign keys with the #OnToMany Maping
instead on traffic_jam_id Generated Value notation change like this,
#GeneratedValue(strategy = GenerationType.AUTO)
Check it :)
In case anyone stumbels upon this, this is the way to go.
The TrafficJamEntity needs to look like this:
#Entity
#Table(name = "traffic_jam", schema = "public", catalog = "dwh")
#Setter
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class TrafficJamEntity {
#Id
#Column(name = "traffic_jam_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "traffic_jam_seq")
#SequenceGenerator(name = "traffic_jam_seq", sequenceName = "traffic_jam_id_sequence", allocationSize = 1)
private long trafficJamId;
#OneToMany(mappedBy = "trafficJamEntity", cascade = CascadeType.PERSIST)
#Builder.Default
private Set<DisturbanceCourseEntity> disturbanceCourseEntitySet = new LinkedHashSet<>();
}
And the child entity Disturbance Course needs to look like this:
#Entity
#Table(name = "disturbance_course", schema = "public", catalog = "dwh")
#IdClass(DisturbanceCourseEntityPK.class)
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class DisturbanceCourseEntity {
#ManyToOne
#Id
#JoinColumn(name = "traffic_jam_id")
private TrafficJamEntity trafficJamEntity;
}
The #Column(name = "traffic_jam_id") Attribute of my question is unnecessary because the #ID Annotation does the same.
Also the binding between the parent-child objects needs to be bidirectional so its not
trafficJamEntity.getDisturbanceCourseEntitySet().add(DisturbanceCourseEntity
.builder()
.latitude(latlong[0])
.longitude(latlong[1])
.orderNumber(shapeId)
.build());
but
DisturbanceCourseEntity disturbanceCourseEntity = DisturbanceCourseEntity
.builder()
.latitude(latitude)
.longitude(longitude)
.orderNumber(shapeId)
.trafficJamEntity(trafficJamEntity)
.build();
trafficJamEntity.getDisturbanceCourseEntitySet().add(disturbanceCourseEntity);
I have two entities and when I want to get grade.getSubcompetence(). Error happens when i save entity, because subcompetence data null. When i debug i see error:
'java.lang.StackOverflowError' exception. Cannot evaluate _$$_jvstea5_f.toString()
grade entity:
#Entity
#Data
#NoArgsConstructor
#ToString(exclude = {"subcompetence"})
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#Table(name = "GRADE")
public class Grade {
#JsonBackReference
#ManyToOne(
fetch = FetchType.LAZY,
optional = false)
#JoinColumn(name = "SUBCOMPETENCE_ID", nullable = false)
private Subcompetence subcompetence;
#JsonBackReference
#ManyToOne(
fetch = FetchType.LAZY,
optional = false)
#JoinColumn(name = "LEVEL_ID", nullable = false)
private Level level;
}
subcompetence entity:
#Entity
#Data
#NoArgsConstructor
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#Table(name = "SUBCOMPETENCE")
public class Subcompetence {
#JsonManagedReference
#OneToMany(
mappedBy = "subcompetence",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Grade> grades;
}
try adding #EqualsAndHashCode(exclude="grades") in your Subcompetence class
Add add #EqualsAndHashCode(callSuper=false) and JsonIgnore like below
#OneToMany(
mappedBy = "subcompetence",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonIgnore
private List<Grade> grades;