How to delete entities in a chained one-to-many and many-to-many JPA relationship - jpa

I have a chain of entities as follows:
#Entity
#Table(name = "Patients")
public class Patient extends TimeStampedPersistable{
private static final long serialVersionUID = 1L;
#OneToMany(mappedBy="patient", cascade = CascadeType.ALL)
#PrivateOwned
private List<Insurance> insurances;
}
#Entity
#Table(name = "insurance")
public class Insurance extends PersistableEntity {
private static final long serialVersionUID = 1L;
#ManyToOne(optional=false)
#JoinColumn(name="patient_id", nullable=false)
private Patient patient;
#Column(name = "policy_number", unique = false, nullable = true)
private String policyNumber;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "insurance_companycodes", joinColumns = {
#JoinColumn(name = "insurance_id", referencedColumnName = "id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "insuranceCompanyCode_id", referencedColumnName = "id", nullable = false, updatable = false) })
private Set<InsuranceCompanyCode> insuranceCompanyCodes = new HashSet<>();
}
#Entity
#Table(name = "insurance_company_codes")
public class InsuranceCompanyCode extends PersistableEntity {
private static final long serialVersionUID = 1L;
#Column(name = "identifier", unique = true, nullable = true)
private String identifier;
#ManyToMany(mappedBy = "insuranceCompanyCodes", fetch = FetchType.LAZY)
private Set<Insurance> insurances = new HashSet<>();
}
I need to remove insurance items from the Patient object. I am using the following code:
for (Iterator<Insurance> iterator = patient.getInsurances().iterator(); iterator.hasNext();) {
iterator.next();
iterator.remove();
}
This seems to work for child entities that don't have child entities, however in this situation the Insurance entity has child entities and is not actually removed (no exceptions are displayed). Note, I am using the EclipseLink specific annotation #PrivateOwned which I expected would have forced the removal of the Insurance entities.
Any guidance, suggestions appreciated!

Related

How to save a copy of a entity

I use spring boot 3, I have this entity structure
#Data
#Entity
#Table
public class Repo {
#Id
#NotNull
private Long repoId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "repo", cascade = {CascadeType.ALL})
private List<Pub> pubs = new ArrayList<>(0);
...
}
#Data
#Entity
#Table
public class Pub {
#Id
#Column
#NotNull
protected Long pubId;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinColumn(name = "type_id")
protected TypeDoc typeDoc;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pub", cascade = {CascadeType.ALL}, orphanRemoval = true)
protected List<Person> persons = new ArrayList<>(0)
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinTable(name = "pub_cat", schema = "", joinColumns = { #JoinColumn(name = "pub_id", nullable = true, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "cat_pub_id", nullable = true, updatable = false) })
private List<CatPub> catPub = new ArrayList<>(0);
}
A repo has a lit of pub, in reality they can have only 2.
So I feed a Pub object..... put it it the list of depot, and i need to create exactly the same and put it in the list of depot.
what is the most effective way to do it?

JPARepository - sometimes create duplicate records

I have the following entity class.
#Data
#EqualsAndHashCode(callSuper=false)
#ToString(callSuper=true)
#Entity
#Table(name = "storeitem")
public class StoreItem extends SellableStoreItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "store_id")
private Store store;
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "storeitemcategory_id", nullable = true)
private StoreItemCategory storeItemCategory;
#EqualsAndHashCode.Exclude
#OneToMany(fetch = FetchType.EAGER, mappedBy = "storeItem")
private List<StoreItemTranslation> storeItemTranslationList = new ArrayList<>();
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "storeItem",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<StoreItemOptionCollectionSelection> storeItemOptionCollectionSelections = new ArrayList<>();
#EqualsAndHashCode.Exclude
#Column(name = "uid")
private UUID uid = UUID.randomUUID();
#EqualsAndHashCode.Exclude
#CreationTimestamp
#Column(name = "createddate", nullable = false)
private LocalDateTime createdDate;
#EqualsAndHashCode.Exclude
#Column(name = "iscurrent", nullable = false)
private boolean isCurrent = true;
And in my service layer, I do the following.
private StoreItemResponse setStoreItemCreate(StoreItemDTO storeItemDTO, Store store, StoreItemCategory storeItemCategory) {
StoreItem storeItem = new StoreItem(storeItemDTO, store, storeItemCategory);
if(storeItemDTO.getUid() != null){
storeItem.setUid(storeItemDTO.getUid());
}
storeItem = storeItemRepository.save(storeItem);
// Create Translations for store Item
for (TranslationDTO translationDTO : storeItemDTO.getTranslationDTOs()) {
StoreItemTranslation translation = new StoreItemTranslation(translationDTO, storeItem);
storeItemTranslationRepository.save(translation);
}
return new StoreItemResponse(storeItem.getId(), DtoResponseStatus.OK);
}
However, when testing the code, I notice that there are times (not often but some cases) I see duplicate records (with different id) are being saved to database. And the duplicates are saved 2ms apart so I suspect storeItem = storeItemRepository.save(storeItem); created the duplicate records.
Why would this happen?

spring data - how to filter users of a particular userProfile by userProfileType?

My User entity follows:
#Entity
#Table
public class User {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "id", unique = true, insertable = true)
private String id;
// non relevant attributes
#ManyToMany(fetch = FetchType.EAGER)
#Fetch(FetchMode.SELECT)
#JoinTable(name = "user2userProfile",
joinColumns = #JoinColumn(name = "userId"),
inverseJoinColumns = #JoinColumn(name = "userProfileId"))
private Set<UserProfile> userProfileSet;
// getters and setters
}
The UserProfile entity follows:
#Entity
#Table(name = "userProfile")
public class UserProfile {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "id", unique = true, insertable = true)
private String id;
#Column(name = "type", length = 15, unique = true, nullable = false)
private String type = UserProfileType.USER.getUserProfileType();
// constructors, getter and setter
}
The UserProfileType enum is
public enum UserProfileType {
USER("USER"),
READER("READER"),
WRITER("WRITER"),
ADMIN("ADMIN");
// constructor and getter
}
My UserJpaRepository is:
public interface UserJpaRepository extends JpaRepository<User, String> {
// non relevant code
List<User> findAllByUserProfileType(UserProfileType userProfileType);
}
The way it stands now, I get the following error message on the console:
org.springframework.data.mapping.PropertyReferenceException: No property userProfileType found for type User!
What is the correct declaration on UserJpaRepository to get a list of users that have a specific UserProfileType (i.e. a list of all users that have a UserProfile of type READER)?
I don't really understand why you need to have a many to many relationship from your user to your user profile.
So if we would correct that to a many to one relationship like:
in User:
#OneToMany(mappedBy = "user")
private Set<UserProfile> profiles;
in UserProfile:
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
you could just setup a search by String type in your UserProfileRepository:
List<UserProfile> findAllByType(String type);
If you now iterate the List you get, you can get all users with a certain UserProfileType:
List<User> users = userProfileRepository.findAllByType(UserProfileType.USER.toString()).stream().map(profile -> profile.getUser()).collect(Collectors.toList());

Hibernate error: mappedBy reference an unknown target entity property

I am having an issue in setting up a many to many relationship in my entities. And I don't understand why
failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: cardgame.bean.User.card in cardgame.bean.Card.users
My Entities:
#MappedSuperclass
#Data
public class BaseEntity implements Serializable {
#Id
#Column(name = "id", nullable = false, unique = true)
private String id;
public BaseEntity() {
this.id = UUID.randomUUID().toString();
}
}
My user emtity:
#Data
#Entity
#Table(name = "users")
public class User extends BaseEntity {
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "uuid", nullable = false)
private String uuid;
#Column(name = "email", nullable = false, unique = true)
private String email;
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Card> cards;
#Column(name = "isActive", nullable = false)
private boolean isActive;
}
My card entity:
#Data
#Entity
#Table(name = "cards")
public class Card extends BaseEntity {
#OneToMany(mappedBy = "card")
private List<User> users;
#Column(name = "strength", nullable = false)
private int strength;
#Column(name = "isActive", nullable = false)
private boolean isActive;
}
The users and cards tables have a many-to-many relationship via user_card table:
#Data
#Entity
#Table(name = "user_card")
public class UserCard implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User user;
#Id
#ManyToOne
#JoinColumn(name = "card_id", nullable = false)
private Card card;
#Column(name = "cardCount", nullable = false)
private int cardCount;
}
What am i doing incorrect. Please help me

java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST

I have two 2 classes in relation many to many.
#Entity
#Table(name = "recipies")
public class Recipie implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String url;
private String image;
#ManyToMany
#JoinTable(
name = "recipie_ingredients",
joinColumns = {
#JoinColumn(name = "recipie_id", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "ingredient_id", referencedColumnName = "id")})
private List<Ingredient> ingredients = new ArrayList<>();
#Entity
#Table(name = "ingredients")
public class Ingredient implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToMany(mappedBy = "ingredients")
private List<Recipie> recipies;
I would like to create a new recipie this way:
List<Ingredient> ingredientsList = new ArrayList<>();
String ingredientName = "example";
Ingredient ingredient = ingredientsDao.findIngredientByName(ingredientName);
if (ingredient == null) {
ingredient = new Ingredient();
ingredient.setName(ingredientName);
}
ingredientsList.add(ingredient);
.....
recipie.setIngredients(ingredientsList);
recipiesDao.addRecipie(recipie);
If ingredient doesn't exist in database, occur errors like this
Caused by: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST
Is there any way to Ingredient objects created in the table automatically?
I try add CascadeType.PERSIST but It also doesn't work
#ManyToMany(mappedBy = "ingredients", cascade = CascadeType.PERSIST)
private List<Recipie> recipies;
First of all, for a bidirectional relationship, both sides need to be updated, so:
recipe.getIngredients().add(ingredient);
ingredient.getRecipes().add(recipe);
Then, you can set the cascade to PERSIST on the side of the relationship which you are passing to save(). So if you are saving the recipe, you should mark the Recipe.ingredients with
#ManyToMany(cascade = CascadeType.PERSIST)
(Side note, it's spelled "recipe", not "recipie")
As mentioned by #Gimby, you need to assign both sides of the relationship.
When dealing with #Many... sided relationships I always initialise the collection (which you've done on one side):
#Entity
#Table(name = "recipies")
public class Recipie implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String url;
private String image;
#ManyToMany
#JoinTable(
name = "recipie_ingredients",
joinColumns = {
#JoinColumn(name = "recipie_id", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "ingredient_id", referencedColumnName = "id")})
private List<Ingredient> ingredients = new ArrayList<>();
...
}
#Entity
#Table(name = "ingredients")
public class Ingredient implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToMany(mappedBy = "ingredients")
private List<Recipie> recipies = new ArrayList<>();
...
}
And then a slight variation in your logic:
String ingredientName = "example";
Ingredient ingredient = ingredientsDao.findIngredientByName(ingredientName);
if (ingredient == null) {
ingredient = new Ingredient();
ingredient.setName(ingredientName);
}
...
// Don't forget to assign both sides of the relationship
recipe.getIngredients().add(ingredient);
ingredient.getRecipies().add(recipe);
recipiesDao.addRecipe(recipe);
This should then cascade persist/update correctly.
The real fun will begin when you try to figure out how to associate a quantity with the ingredient...