Hibernate not setting foreign keys - java

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

Related

Map id contained in a json to the entity field of a class

Say I have a column books in table authors that contains a list of book ids and some description in jsonb type.
I've managed to deserialize this and place it in an Author instance by defining the following classes:
#Entity
#Table(name = "authors")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Author {
#Id
#Column(name = "id")
private Long id;
#Type(type = "jsonb")
#Column(name = "books", columnDefinition = "jsonb")
private List<TestObject> testObjectList;
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class TestObject {
#JsonProperty("book_id")
private Long id;
#JsonProperty("book_description")
private String bookDescription;
}
However, instead of the Long id field, I'd like to fetch the whole Book entity inside TestBook class. How can I achieve this? This would be easier if the book_id field was inside the Author class, but here it is nested.

Mapping two fields of the same class with One-to-One relationship

I have a Flight class and a AircraftReport class. The AircraftReport class contains an inbound flight and an outbound flight which I would both like to be mapped as a #OneToOne. How do I correctly define the relationship?
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Entity
#Table
public class Flight implements Serializable {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "flight_sequence"
)
#SequenceGenerator(
name = "flight_sequence",
allocationSize = 1
)
#Column(nullable = false, updatable = false)
private Long id;
private String callsign;
private Date date;
private String origin;
private String destination;
private String registration;
private String aircraftType;
#OneToOne(mappedBy = "--what should it be mapped by here--")
private AircraftReport aircraftReport;
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table
public class AircraftReport implements Serializable {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "taxsheet_sequence"
)
#SequenceGenerator(
name = "taxsheet_sequence",
allocationSize = 1
)
#Column(nullable = false, updatable = false)
private Long id;
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "inbound_flight_id")
private Flight inboundFlight;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "outbound_flight_id")
private Flight outboundFlight;
...
}
IMHO you don't need to use a bidirectional relationship for a OneToOne mapping. If you want the AircraftReport just get the AircraftReport and the Flight instances will come with it. Otherwise if you're not working with a report just get the Flight and be happy.
Also, I don't recommend using Cascade operations on a OneToOne, or at any time for that matter. Do you really ever want the Flight deleted?
Map to either inboundFlight or outboundFlight. You can't have one bidirectional relationship take the place of two of them. If you want two bidirectional relationships then add another to Flight.
#OneToOne(mappedBy = "inboundFlight")
private AircraftReport aircraftReportInbound;
#OneToOne(mappedBy = "outboundFlight")
private AircraftReport aircraftReportOutbound;
As you have two separate relationships from AircraftReport two Flight, you also have to separate relationships from Flight to AircraftReport.
#OneToOne(mappedBy = "inboundFlight")
private AircraftReport aircraftReportInbound;
#OneToOne(mappedBy = "outboundFlight")
private AircraftReport aircraftReportOutbound;

Hibernate #Where clause does not work on a joined relationship. (Hibernate)

Currently I have two entities, Hoa and Activity respectively. They share the following relationship
HOA:
#Data
#Entity
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Hoa {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Builder.Default
#OneToMany(mappedBy = "hoa", orphanRemoval = true)
#Cascade(CascadeType.ALL)
#Where(clause = " time > now() ")
private List<Activity> activityDashboard = new ArrayList<>();
Activity:
#Data
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Where(clause = "time > now()")
public class Activity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Builder.Default
private LocalDateTime time = LocalDateTime.now();
#NotNull
#ManyToOne
#JoinColumn(name = "HOA_ID")
#JsonIgnore
#ToString.Exclude
private Hoa hoa;
}
The use of #Where here suggests two things:
1.) When I try to get the associated activity entity from the repository directly it should not return anything.
2.) When I get the HOA entity from the repository, it should also not have the associated activity object within the list.
With this being said, The last assertion in the following test is failing.
#Test
void lateActivitiesShouldNotBeDisplayed() {
Activity sut = activityService.createActivity(activityInThePast,1L);
assertThat(activityRepository.findAll()).isEmpty();
Hoa sutHoa = hoaRepository.findById(sut.getHoa().getId()).get();
assertThat(sutHoa.getActivityDashboard()).isEmpty();
}
The sutHoa object has the associated activity within the activityDashboard list. Why could this be happening?

Deleting an entity with one to one relation

My two entities have one to one relation
#Getter
#Setter
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
#Table(uniqueConstraints = #UniqueConstraint(columnNames = "email"), name = "library_user")
public class AppUser {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
#EqualsAndHashCode.Exclude
private Long id;
// other fields
#OneToOne(mappedBy="user", cascade={CascadeType.REMOVE,CascadeType.PERSIST}, orphanRemoval = true)
private PasswordResetToken token;
// getters/setters and equals/hashcode
}
#Getter
#Setter
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
#Table(name = "password_reset_token")
public class PasswordResetToken {
private static final int EXPIRATION = 60 * 24;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
#OneToOne(targetEntity = AppUser.class, fetch = FetchType.EAGER, cascade={CascadeType.REMOVE,CascadeType.PERSIST}, orphanRemoval = true)
#JoinColumn(nullable = false, name = "user_id")
private AppUser user;
// getters/setters and equals/hashcode
I tried to delete my user entity by this method
public void deleteUser(Long id) {
resetTokenRepository.deleteAllByUserId(id);
userRepository.deleteById(id);
}
PasswordResetTokenRepository class which method I called in my service method, for deleting user I used regular hibernate method deleteById(Long id)
#Repository
public interface PasswordResetTokenRepository extends JpaRepository<PasswordResetToken, Long> {
void deleteAllByUserId(Long id);
}
But when I try to delete by this method I got this error:
not-null property references a null or transient value : kpi.diploma.ovcharenko.entity.user.PasswordResetToken.user
I read several websites how to delete one to one relation, but their advices didn't help me. For example, I tried a lot of variants of annotation cascade={CascadeType.ALL}, tried all the variants(CascadeType.REMOVE,CascadeType.PERSIST and so on), all time I got the same error. Help me pls, to understand what I do wrong.
try this:
#OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
Here is complete explication .

Composite primary key table not populating in related entity

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.

Categories