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

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?

Related

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;

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 .

JPA #OneToMany and #ManyToOne: back reference is null though mappedBy is included

I have 4 Entities, that a related to each other with #OneToMany relationships.
When I try to save Order that contains OrderItem - Orderitem has no backreference.
In the code below only important fields are showed for brevity ( usual strings and primitives are omitted ). I decided to include Dish and User Entities also.
Order:
#Entity
#NoArgsConstructor
#Getter
#Setter
#ToString
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToMany(
mappedBy = "order",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true)
private List < OrderItem > orderItems;
}
Dish:
#Entity
#NoArgsConstructor
#Getter
#Setter
#ToString
public class Dish {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "dish")
#ToString.Exclude
private List < OrderItem > orderItems;
}
OrderItem:
#Entity
#NoArgsConstructor
#Getter
#Setter
#ToString
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#ToString.Exclude
private Dish dish;
#ManyToOne(fetch = FetchType.LAZY)
private Order order;
private int quantity;
}
User:
#Entity
#NoArgsConstructor
#Getter
#Setter
#ToString
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List < Order > orders;
}
The problem happens when I try to save Order with Spring data JPA.
Let's print Order to see OrderItem before saving.
public Order saveOrder(Order order) {
System.out.println("SERVICE saving order " + order);
return orderRepository.save(order);
}
As you can see, orderItems backreference is null before saving ( I though spring data jpa should deal with setting it ).
SERVICE saving order Order(id=0,
orderItems=[OrderItem(id=0, quantity=2, order=null)])
Here is what I have in DB ( Order and OrderItem entities ).
In your OrderItem class, add annotation below:
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST}, fetch=FetchType.LAZY)
#JoinColumn(name="order_id", referencedColumnName="id", nullable = false)
Order order.
One more thing, I suggest you use SEQUENCE_GENERATOR, beacause IDENTITY means: I'll create the entity with a null ID and the database will generate one for me. I don't think Postgres even supports that, and even if it does, a sequence generator is a better, more efficient choice.
The best option that I found for this is doing something like:
order.getOrderItems().forEach(orderItem -> orderItem.setOrder(order));
Before your save() call. Even though order is not persisted at this point, it seems like Hibernate can resolve the relation and the back references will be set correctly.
If you do not want to bother setting the back reference in your business logic, you can add something like this to your entity:
class Order {
...
#PrePersist
public void prePersist() {
setMissingBackReferences();
}
private void setMissingBackReferences() {
orderItems.forEach(oderItem -> {
if (oderItem.getOrder() == null) {
oderItem.setOrder(this);
}
});
}
...
}

Java Spring Data App doesn't save sub-objects

I'm trying to build build service, which saves object with sub-objects, but getting error. In result object data fields saved, but sub-object not.
I have the next object. The main is Order and sub-object is Partner:
#Getter
#Setter
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private int orderId;
#OneToMany(mappedBy = "order", fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private Set<Partner> partners;
}
#Getter
#Setter
#Entity
#Table(name = "partners")
public class Partner implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "partner_id")
private int id;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
I use standard embedded method "save" from Spring Jpa Repository:
#Repository
public interface OrdersRepository extends JpaRepository<Order, Integer> {
}
and service, which call this Repository:
#Service
public class OrdersServiceImpl implements OrdersService {
#Autowired
private OrdersRepository repository;
#Override
public Order save(Order order) {
return repository.save(order);
}
}
Does someone have an idea why Partners are not saved?
Thanks a lot!
Because the relationship owner is Partner, so that you need to save the Order first. Or you can put cascade = CascadeType.PERSIST on private Order order;

Mapping one to many relationship in separate table

I am trying to develop a system for managing dormitories. Since I don't have much experience with databases, I am stuck on a problem and I have a solution but I am not sure if this would be the right approach.
So I have Room and User. Each user can be accommodated in one room, but one room can accommodate more users. I would like to manage this relationship in one entity - Accommodation. Here I would have more properties, like start/end Date, etc.
I am using Hibernate to map the tables. From what I've read, persisting Collections from Java can be done in two ways, either by #OneToMany or by #ElementCollection. I am not quite sure if I should define this relationship in the Room entity or in the Accommodation entity? If I do it in the room entity then the Accommodation would hold just fk from the room/user tables?
Also, is it possible to only fetch the primary key when doing one-to-many relations instead of getting the whole object? I know that FETCH.LAZY does this, but in my Accommodation entity ideally I would want to store only Set studentsIds.
Thank you in advance.
#Table(name = "student")
#AllArgsConstructor
#Data
#Embeddable
#NoArgsConstructor
#javax.persistence.Entity
public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "role")
private String role;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "room", nullable = false)
private Room room_number;
}
Here is the Room entity
#javax.persistence.Entity
#Table(name = "room")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Data
public class Room
{
#Id
#Column(name = "room_number")
private Long roomNumber;
#Column(name = "location_address")
private String locationAddress;
#Column(name = "dormitory_name")
private String dormitoryName;
}
Accommodation entity
#javax.persistence.Entity
#Table(name = "accommodation")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Data
public class Accommodation extends Entity {
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "room_number")
private Room room_number;
#OneToMany(mappedBy = "room_number", // I am not sure about this
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true) private List<Student> students;
#Column(name = "date_from")
private Date dateFrom;
#Column(name = "date_to")
private Date dateTo;
}

Categories