How to cascade delete unidirectional - spring

I have a case where I have a user and the user had an EmailVerificationToken.
I would like to delete the EmailVerificationToken when the user gets deleted.
However, since the EmailVerificationToken is an object that is only needed for a short period of time (ie only used once and is irrelevant after), I don't want the User entity to contain the token. Instead, I want the EmailVerificationToken to reference the user it belongs to, but not the other way around.
How do I set it up so when I delete the user, it deletes the EmailToken even though it doesn't reference it in the User entity?
This is the code I have currently:
public class EmailVerificationToken implements IEntity, IDto {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "emailVerificationTokenId")
private Long id;
#OneToOne
#JoinColumn(name = "userId", nullable = false)
private User user;
}
and
public class User implements IEntity, IDto, UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "userId")
private Long id;
}

I am guessing you have a transactional service which handles the deletion of a User.
You need to add a named query in your EmailVerificationToken class. Something like
#NamedQuery(name = EmailVerificationToken.FIND_BY_USER, query = "Select e from EmailVerificationToken e where e.user =:user"),
while adding a constant in your class for the name of the query, like:
public static final String FIND_BY_USER = "EmailVerificationToken.FindByUser";
Then you need to define a service which finds a managed instance of your token class with the given User instance.
Then in the transactional method, where you would delete the user, first delete the token;
public void deleteUser(User user){
EmailVerificationToken token = someService.findByUser(user); //you get a
//managed instance using the previously defined query
em.remove(token);
em.remove(user);
}
em is an instance of the Entity manager.
Hopefully this helps you. For any further questions, you are free to ask.

Related

I'm using #UniqueConstraint in the below code.when im using saveall() method

I'm using #UniqueConstraint in the below code.when im using saveall()
method to save the data,when constraint violation fails for the first
data remaining values are also not saving.
#Entity
#Data
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "tradeStartTime", "tradeEndTime", "contract" }))
public class TradingHours {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private ZonedDateTime tradeStartTime;
private ZonedDateTime tradeEndTime;
#ManyToOne
#JoinColumn(name = "contract")
private Contract contract;
}
I think there is no way to continue bulk insert after transaction fail. If you need to save all remaining values, you should save each value separately in save() method marked as #Transactional(propagation = Propagation.REQUIRES_NEW), take a look at docs.
You also have to catch DataIntegrityViolationException somewhere until it breaks somthenig else.

JPA OneToOne UPDATE instead of INSERT

I am new to Spring/JPA and I am trying to use Database relationship #annotations to simplify my code.
I have two entities, a User and Token entity.
When setToken() is called on the User, I want the Token to overwrite any old tokens associated with the user in the table.
At the moment, the new token (via user.setToken()) is being INSERT rather than UPDATE.
How do I go about achieving this relationship? In "Lame-mans"...A user can only even have one token and can be given another at any time, discarding the old.
There are extra fields in these models that i have truncated for clarity.
#Entity
#Table(name = "Users")
public class User {
#Column(name = "USER_ID")
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonInclude(JsonInclude.Include.NON_NULL)
private Long userId;
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinColumn(name = "REFRESH_TOKEN_ID")
private RefreshToken refreshToken;
...setters and getters
And the code for a token:
#Entity
#Table(name = "RefreshTokens")
public class RefreshToken {
#Column(name = "REFRESH_TOKEN_ID")
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long tokenId;
If your RefreshToken has only 1 Field REFRESH_TOKEN_ID which is long. Why you need different table for this. You can just have something like this
public class User {
#Column(name = "USER_ID")
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonInclude(JsonInclude.Include.NON_NULL)
private Long userId;
#Column(name = "REFRESH_TOKEN_ID")
private Long refreshToken;
...
When setToken() is called you must be setting any value. If yes thats it. Your logic will work fine,
If no you can always generate a Unique value in Java based on Current time or something else.
or
If you want to continue with same patter use orphanRemoval = true
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "REFRESH_TOKEN_ID")
private RefreshToken refreshToken;
You should never modify the primary key of a an entity
This is not be possible as you change the id of the token. You need to create a different id in the RefreshToken thats unique and stays the same after save.
If you really do need that - you'd better of delete the entity and create a new one which just copies the old one but with a new primary key.

Delete object in OneToOne relationship ConstraintViolationException

I'm trying to delete entity which is the owner of the relationship, but I am getting an exception as follows:
org.h2.jdbc.JdbcSQLException: Referential integrity constraint
violation: "FKS59BBPCYQ1GUKBWWA61TYF8YF: PUBLIC.RESERVATIONS FOREIGN
KEY(CAR_LICENSE_ID) REFERENCES PUBLIC.CARS(LICENSE_ID) ('EPA13S')";
SQL statement:
I know that is because of trying to delete an object to which another one has a reference with fk_key. Here is my model:
public class Reservation
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Enumerated(EnumType.STRING)
private DriverType driverType;
private LocalDateTime startTime;
private LocalDateTime stopTime;
#OneToOne(cascade = CascadeType.PERSIST)
private Car car;
private BigDecimal cost;
}
public class Car
{
#Id
#NonNull
#Size(min = 6, max = 6)
String licenseId;
#OneToOne(mappedBy = "car", cascade = CascadeType.REMOVE)
Reservation reservation;
}
How could I possibly deal with such scenario? I would like to delete car from parking when the reservation ends as I don't need it and having license id as pk_key make it vulnerable for trying to insert new car with upcoming reservation even though the previous one has ended.
Deleting car:
carRepository.deleteByLicenseId(reservation.getCarLicenseId());
#Query("DELETE FROM Car c where c.licenseId = :licenseId")
void deleteByLicenseId(#Param("licenseId") String licenseId);
I assume you are extending Spring CrudRepository<Reservation, Long>
The joinColumn is on the Reservation side and from what I can see what you want to do is to delete the Reservation as well. So why not delete it from the owning side, which looks like the reservation.
Change Reservation to.
#OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
Rather use the following for delete.
reservationRepository.delete(reservation);
This is a bidirectional relationship. Add this to Car class:
public void removeReservation() {
if (this.reservation != null) {
this.reservation.setCar(null);
this.reservation = null;
}
}
Call car.removeReservation().
Then delete car.

Fetch specific property in hibernate One-to-many relationship

I have two pojo classes with one-to-many relationship in hibernate
CustomerAccountEnduserOrderDetails.class
#Entity #Table(name="customer_account_enduser_order_details")
public class CustomerAccountEnduserOrderDetails implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Long id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "product_id", insertable = false, updatable = false)
private CustomerCmsProduct customerCmsProduct;
}
Second is CustomerCmsProduct.class
#Entity
#Table(name="customer_cms_product")
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomerCmsProduct {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private Long id;
#Column(name="name")
private String name;
#Column(name="offer_price")
private String offerPrice;
#Column(name="original_price")
private String originalPrice;
#Column(name="discount")
private String discount;
}
Here if I fetch the object of CustomerAccountEnduserOrderDetails class,then i will get the CustomerCmsProduct class also , my problem is that here i want the specific column of CustomerCmsProduct table (not all by default i am getting all) like only id and originalPrice.
How i can do like that projection here?
In the service layer or at a webservice layer( if this is a web project) Create two different classes other than #Entity as DTO(Data Transfer Objects) which helps is data transfer from one layer to the other.
public class CustomerAccountEnduserOrderDetailsPojo {
private List<CustomerCmsProductPojo> productPojoList = new ArrayList<> ();
// getter and setter
}
public class CustomerCmsProductPojo {}
Follow the below steps
Retrieve the #Entity class data by executing the query from service layer.
Iterate over all the fields and copy only required fields to pojo layer
Expose the data to other layers using service method.
This way, we can avoid changing the custom hibernate behavior as it is linked with many parameters like cache, one to many queries that are fired per iteration.
And also, do any customization that you want in this layer. Hope this is multi layered project where you have different layers which servers different purpose.

JPQL Special Query

I have two entity bean :
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<Comment>();
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
and my Comment class is like this :
#Entity
public class Comment {
#Id
#GeneratedValue
private Long id;
private String title;
private String content;
#ManyToOne
private User user
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
now I know that I can get the User Object from session and set the user for my comment like this in order to be able to use the join feature in JPA:
commentObject.setUser(TheSessionGrabedUserObject/UserObjectWhichHasFetchedFromDbUsingUserId);
but as long as I have the userId for my user Object I do not need to do this.
I'm looking for a way to insert this foreignKey into my comment table without getting the User Object from session or maybe query to database to fetch it first !
how I'm gonna do it using JPQL ?
You can use the entityManager.getReference() method. In your case:
entityManager.getReference(User.class, userId);
This will not perform any DB query, but will give you a User instance with only the ID populated, and you can pass that to commentObject.setUser().

Resources