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

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

Related

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

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!

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?

How to fetch entities by objects value in JPA criteria api query

I am using JPA with JSF datatable with lazy loading.
Here One car can be owned by many users. So when i logged in to the application i want the cars which is owned by the user logged in(assume it as userId=1).
I have a mapping table "Cars_User" that contains carId and userId columns.
My Entities are like this
My Car Class
#Entity
#Table(name="car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
#Transient
private boolean myCar;
#NotNull
#Size(min = 1, max = 50)
public String name;
#OneToMany(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinTable(name = "Cars_User", joinColumns = #JoinColumn(name = "carId"), inverseJoinColumns = #JoinColumn(name = "userId"))
private List<User> carUsers = new ArrayList<User>();
getters ...
setters ...
}
User Class
#Entity(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstName;
private String lastName;
}
I have found one answer for Lists of String collection in this link but how can be achieved in my case.
I wanted to do get all Cars entities in criteria api that contains the logged in user id "userId" in carUsers Lists. can anyone please help?
I found the solution. I have passed the logged in user Object "user" in isMember function. This may help for somebody.
CriteriaBuilder criteriaBuilder = em.getEntityManagerFactory().getCriteriaBuilder();
CriteriaQuery<Car> criteria = criteriaBuilder.createQuery(Car.class);
Root<Car> root = criteria.from(Car.class);
criteria.where(criteriaBuilder.isMember(user, root.get(Car_.carUsers)));
List<Car> cars = em.createQuery(criteria).getResultList();

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...