How to fetch latest association with complex primary key? - java

I need to map make #OneToMany association as one entity (#ManyToOne or #OneToOne) which has the latest field
#Entity
#Getter
#Setter
#Table(name = "trips")
public class TripEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "trips_sequence")
#GenericGenerator(name = "trips_sequence",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "trips_seq"),
#Parameter(name = "initial_value", value = "100"),
#Parameter(name = "increment_size", value = "1")
})
#Column(name = "trip_id")
private Long id;
//Initially it was such mapping:
//#OneToMany(orphanRemoval = true, mappedBy = "trip", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
//private Set<TripTelemetryEntity> tripTelemetry = new HashSet<>();
//Finally I need something like this
//#ManyToOne or #OneToOne
private TripTelemetryEntity lastTripTelemetry;
}
#Entity
#Getter
#Setter
#IdClass(TripTelemetryEntity.TripTelemetryId.class)
#Table(name = "trip_telemetry")
public class TripTelemetryEntity implements Serializable {
#Id
#Column(name = "trip_id")
private Long tripId;
#ManyToOne
#JoinColumn(name = "trip_id", insertable = false, updatable = false)
private TripEntity trip;
#Id
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "occurred_on")
private Date occurredOn;
#Override
public TripTelemetryId getId() {
return new TripTelemetryId(tripId, occurredOn);
}
/**
* This represents a "composite primary key" for the trip_telemetry table.
*/
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public static class TripTelemetryId implements Serializable {
private static final long serialVersionUID = 1L;
protected Long tripId;
protected Date occurredOn;
}
}
if I add
#JoinFormula("(SELECT r.id FROM trip_telemetry r WHERE r.trip_id = trip_id ORDER BY r.occurred_on DESC LIMIT 1)")
so I catch exception:
Caused by: org.hibernate.AnnotationException: A Foreign key refering com.app.core.domain.TripTelemetryEntity from com.app.core.domain.TripEntity has the wrong number of column. should be 2
If I do so:
#ManyToOne
#Where(clause = "occurred_on = now()")
private TripTelemetryEntity tripTelemetry;
I catch exception:
org.postgresql.util.PSQLException: ERROR: column tripentity0_.triptelemetry_occurred_on does not exist

Related

Hibernate LazyInitializationException

I am trying to get the versions field within my MachineGroup Entity that is ManyToMany relationship. I am trying to fetch it in a custom serializer using ObjectMapper but for some reason I always get the LazyInitializationException - org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.altair.autoTester.entities.machinegroup.MachineGroup.versions, could not initialize proxy - no Session.
MachineGroup
#Entity
#Table(name="machine_groups")
#Getter
#Setter
#AllArgsConstructor(access = AccessLevel.PUBLIC)
#NoArgsConstructor
public class MachineGroup {
#Id
#GeneratedValue(strategy= GenerationType.AUTO, generator = "machine_groups_seq")
#SequenceGenerator(name = "machine_groups_seq", allocationSize = 1, initialValue = 2)
#Column(name = "id")
private long id;
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "machine_groups_to_versions",
joinColumns = #JoinColumn(name = "machine_group_id"),
inverseJoinColumns = #JoinColumn(name = "version_id"))
#JsonManagedReference(value="machineGroups-versions")
private Set<Version> versions = new HashSet<>();
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "machine_groups_to_tr_types",
joinColumns = #JoinColumn(name = "machine_group_id"),
inverseJoinColumns = #JoinColumn(name = "tr_type_id"))
private Set<TrType> trTypes = new HashSet<>();
}
Version.java
#Entity
#Table(name="versions")
#Getter
#Setter
public class Version {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "versions_seq")
#SequenceGenerator(name = "versions_seq", allocationSize = 1)
#Column(name = "id")
private long id;
#Column(name = "name", unique = true)
private String name;
#Column(name = "creation_time")
private Date creationTime;
#Column(name = "exe_file")
#Lob
private Blob exeFile;
#JsonBackReference(value="machineGroups-versions")
#ManyToMany(mappedBy = "versions", cascade = CascadeType.MERGE)
private Set<MachineGroup> machineGroups = new HashSet<>();
public Version(){};
public Version(String name) {
this.name = name;
}
}
MachineGroupSerializer.java
public class MachineGroupSerializer extends StdSerializer<MachineGroup> {
public MachineGroupSerializer() {
this(null);
}
public MachineGroupSerializer(Class<MachineGroup> t) {
super(t);
}
#Transactional
#Override
public void serialize(MachineGroup machineGroup,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("id",machineGroup.getId());
jsonGenerator.writeObjectField("versions", machineGroup.getVersions().stream().map(Version::getId).collect(Collectors.toSet()));
jsonGenerator.writeObjectField("trTypes", machineGroup.getTrTypes().stream().map(TrType::getId).collect(Collectors.toSet()));
jsonGenerator.writeEndObject();
}
}
TrTypes.java
#Table(name = "tr_types", indexes = {
#Index(name = "tr_types_type_name_uindex", columnList = "type_name", unique = true)
})
#Entity
#AllArgsConstructor(access = AccessLevel.PUBLIC)
#NoArgsConstructor
public class TrType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "type_name", nullable = false)
private String typeName;
}
I put the JsonManagedReference and JsonBackReference so I won't have infinite loop calling one another, not sure if it's causing any issues.
Calling the getTrTypes() function works but has the EAGER fetchType which I do not want to add to the versions collection.
What can cause this lazy exception to occur and how can I prevent it?
Also the function calling the serialzier within my MachineGroupService has the #Transactional annotation, I saw something that is related to the hibernate session that might be close at the serializer level.

How to map bidirectional #OneToMany and #OneToOne on the same entity

I have two enteties and I want to be able access one of them from another and vise versa (bidirectional). But sometimes when persisting an order as stopLossOrder it's not saved to position. If you have any ideas of how it can be implemented (if it's possible this way or similar) I would be glad to hear.
Later on I want to add more orders to position entity similarly to 'stopLossOrder'
#Entity(name = "Position")
#Table(name = "positions")
#Getter #Setter
public class PositionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true)
#JoinColumn(name = "stop_loss_order_id", referencedColumnName = "id")
private OrderEntity stopLossOrder;
#OneToMany(mappedBy = "position",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<OrderEntity> orders = new HashSet<>();
public void setStopLossOrder(OrderEntity stopLossOrder) {
this.stopLossOrder = stopLossOrder;
stopLossOrder.setPosition(this);
}
public boolean addOrder(OrderEntity orderEntity) {
orderEntity.setPosition(this);
return orders.add(orderEntity);
}
}
#Entity(name = "Order")
#Table(name = "orders")
#Getter
#Setter
public class OrderEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = -1462587657644552577L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(optional = false)
private PositionEntity position;
}
Persisting:
if (orderEntity.getType() == OrderType.STOP_MARKET) {
positionEntity.setStopLossOrder(orderEntity);
} else {
boolean isAdded = positionEntity.addOrder(orderEntity);
if (!isAdded)
throw new TradeServiceException("Order with id: " + order.getOrderId() + " already added to position.");
}
orderEntity = orderRepository.save(orderEntity);

Hibernate joining tables with multiple primary keys

I have figured out how to join 2 tables with single primary key. But now I need to join 4 tables and some of these table with composite primary keys.
Here is my table picture
And I want to join them, so I generate classes for them:
// Record
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "record")
public class Record implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "student_id")
private Integer studentId;
#Id
#Column(name = "exam_id")
private Integer examId;
#Column(name = "total_score")
private Integer totalScore;
#Column(name = "student_score")
private Integer studentScore;
#Column(name = "submission_id")
private Integer submissionId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id")
private Student student;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "submission_id")
private Submission submission;
}
// Submission
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "submission")
public class Submission implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "submission_id")
private Integer submissionId;
#Id
#Column(name = "question_id")
private Integer questionId;
#Column(name = "stu_answer")
private String stuAnswer;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "submission")
private Record record;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "submission")
private Set<Question> question;
}
// Question
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "question")
public class Question implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "question_id")
private Integer questionId;
#Column(name = "content")
private String content;
#Column(name = "score")
private Integer score;
#Column(name = "is_delete")
private Integer isDelete;
#Column(name = "option_id")
private Integer optionId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "submission_id")
private Submission submission;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "optional_id")
private Optional optional;
}
// Optional
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "optional")
public class Optional implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "option_id")
private Integer optionId;
#Column(name = "content")
private String content;
#Column(name = "is_delete")
private Integer isDelete;
#Column(name = "answer")
private String answer;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "optional")
private Question question;
}
// Final class to store information
public class RcdSubQuesOpt {
private Integer studentId;
private Integer examId;
private Integer questionId;
private String stuAnswer;
private String qContent;
private String oContent;
private String answer;
}
And this is code for JPA
#Override
public List<RcdSubQuesOpt> getRcdSubQuesOpt(int studentID, int examId) {
Session session = this.getSession();
List<RcdSubQuesOpt> results;
Transaction transaction = null;
try {
transaction = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<RcdSubQuesOpt> criteriaQuery = criteriaBuilder.createQuery(RcdSubQuesOpt.class);
// Try to join tables
Root<Record> pRoot = criteriaQuery.from(Record.class);
pRoot.join("submission", JoinType.INNER);
pRoot.join("question", JoinType.INNER);
pRoot.join("optional", JoinType.INNER);
criteriaQuery.multiselect(
pRoot.get(columns in RcdSubQuesOpt Class......));
// Try to add constraints
Predicate predicate = pRoot.get("examId").in(Arrays.asList(1));
criteriaQuery.where(predicate);
// try to do queries
results = session.createQuery(criteriaQuery).getResultList();
transaction.commit();
} catch (Exception e) {
results = null;
if (transaction != null) {
transaction.rollback();
}
} finally {
session.close();
}
return results;
}
But hibernate throw error as following:
Enitial SessionFactory creation failedA Foreign key refering com.domain.Submission from com.domain.Record has the wrong number of column. should be 2
Exception in thread "main" java.lang.ExceptionInInitializerError
I think it's the composite primary keys problem. But solution I searched is not suitable to solve it. Anyone give me some advice? Thanks!
To reference a composite primary key, you need to use #JoinColumns (plural).
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "sub_submission_id", referencedColumnName = "submission_id"),
#JoinColumn(name = "sub_question_id", referencedColumnName = "question_id")
})
private Submission submission;
However, I must admit, I don't understand your model - especially why Submission has a composite PK with question_id. It looks that one Submission has many Questions, why to include question_id as part of Submission PK?
Perhaps, I'm missing something, because the diagram is not fully visible.

JPA composite key one to may relationship separate table

I am working on mapping a relationship using a composite key, but the composite key is a separate table.
Car Table
car Id
car description
car Value
SafetyReport Table
safetyReport Id
safetyReport Date
safetyReport Value
CompositeKey CarSafetyReport Table
carid
safetyReportId
One To Many: A car will have many safety reports
JPA:
#Data
#Entity
#Table(name = "CarTable")
public class CarEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CarId", nullable = false)
private int carId;
#Column(name = "CarDescription", nullable = false)
private String carDescription;
#Column(name = "CarValue", nullable = false)
private String carDescription;
}
#Data
#Entity
#Table(name = "SafetyReport")
public class SafetyReportEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "SafetyReportId", nullable = false)
private int SafetyReportID;
#Column(name = "SafetyReportDate", nullable = false)
private OffsetDataTime date;
#Column(name = "SafetyReportValue", nullable = false)
private String safetyReportValue;
}
#Data
#Entity
#Table(name = "CarSafetyReport")
public class CarSafetyReportEntity implements Serializable {
#EmbeddedId
private CarSafetyReportPk id;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Embeddable
public class CarSafetyReportPk implements Serializable {
#Column(name = "CarId", nullable = false)
private int carId;
#Column(name = "SafetyReportId", nullable = false)
private int SafetyReportId;
}
I tried
public class CarEntity {
#OneToMany(mappedBy = "id.carId" )
private List<CarSafetyReportEntity> carSafetyReportEntity;
}
I also tried putting the relationship in the composite key CarSafetyReportPk but i got an error for the annotation #OneToMany in a composite key.

Soft Delete : Child Entity not being deleted after Deleting parent Entity in #OneToMany relation in Spring Boot JPA Hibernate

I'm trying to implement soft delete between a parent and child entity in such a way that deleting the parent entity would delete all its child entities as well.
My parent entity is User with a #OneToMany relation with Profile. Profile is created with a reference to User. Deleting the User works fine but when trying to retrieve the Profile that was referencing the User, I get an exception.
{
"title": "Internal Server Error",
"status": 500,
"detail": "Unable to find com.user.domain.User with id 951; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.user.domain.User with id 951",
"path": "/api/profiles",
"message": "error.http.500"
}
User.java
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
#EqualsAndHashCode(exclude = {"users"})
#ToString(exclude = {"users"})
#Getter
#JsonDeserialize(builder = Profile.Builder.class)
#Entity
#Table(name = "users")
#SQLDelete(sql="Update users SET deleted = 'true' where id=?")
#Where(clause="deleted != 'true'")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "password")
private final String password;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "email")
private String email;
#Enumerated(EnumType.STRING)
#Column(name = "gender")
private Gender gender;
#OneToMany(mappedBy = "users", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, orphanRemoval = true)
private Set<Profile> profiles = new HashSet<>();
#Column(name="deleted")
String deleteFlag;
}
Profile.java
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
#EqualsAndHashCode(exclude = {"users"})
#ToString(exclude = {"users"})
#Getter
#JsonDeserialize(builder = Profile.Builder.class)
#Entity
#Table(name = "profile")
#SQLDelete(sql="Update users SET deleted = 'true' where id=?")
#Where(clause="deleted != 'true'")
public class Profile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private final Long id;
#Column(name = "profile_name")
private final String profileName;
#Column(name = "date_of_birth")
private final LocalDate dateOfBirth;
#Column(name = "health_history")
private final String healthHistory;
#ManyToOne
#JoinColumn(name = "users_id")
#JsonIgnoreProperties("reports")
private final User users;
#Column(name="deleted")
String deleteFlag;
}
There is a problem in delete query for profile
#SQLDelete(sql="Update users SET deleted = 'true' where id=?")
Here you're updating users table. I think it's a problem why your profiles are not removed after deleting User.

Categories

Resources