JPA dataIntegrityViolationException occurs for multiple parents - jpa

I am working on simple spring security demo and want to put user and role info into db. Here is the simple structure of my entity.
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long userId;
#OneToMany(mappedBy = "users", fetch = FetchType.LAZY, orphanRemoval=true, cascade = CascadeType.ALL)
private List<UserRoleMapping> userRoleMapping;
}
//
#Entity
#Table(name = "user_role_mapping")
public class UserRoleMapping {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_role_mapping_id")
private Long userRoleMappingId;
#ManyToOne(fetch = FetchType.LAZY)
private Users users;
#ManyToOne(fetch = FetchType.LAZY)
private UserRole userRole;
}
//
#Entity
#Table(name = "users_role")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private Long roleId;
#Column(name = "role")
private String role;
#Column(name = "role_desc")
private String roleDesc;
#OneToMany(mappedBy = "userRole", fetch = FetchType.LAZY, orphanRemoval=true)
private List<UserRoleMapping> userRoleMapping;
}
In my use case, I have to make sure when the user is created then I have to insert subsequence userRoleMapping. And If the userRole is removed then the userRoleMapping must be deleted as well.
So I put CascadeType.ALL and orphanRemoval=true in users entity, and orphanRemoval=true in userRole entity.
However, when I run userRoleRepository.delete(userRole). I have dataIntegrityViolationException.
I did some researches on it and understand it is a kind of jpa constrains to make sure we delete the parents (users) as well.
May I ask if there are any workaround for my use case?
Thanks

Here is the solution to my use cases. Instead of using OneToMany in both entity, I should use ManyToMany relationship. And it is quite make sense as I don't care (for now) the mapping in java logic. Here is my code
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long userId;
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST })
#JoinTable
private Set<UserRole> userRole;
}
//
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private Long roleId;
#Column(name = "role")
private String role;
#Column(name = "role_desc")
private String roleDesc;
#OneToMany(mappedBy = "userRole", fetch = FetchType.LAZY)
private Set<Users> users;
}

Related

JPA foreign key without entity

I'm trying to create a favorites table between a user and a business, but I don't want to use the entire entity, just the ids, how can this be done in JPA? Something like this:
#Data
#Entity
#Table(name = "favorite", schema = "public" , uniqueConstraints = {
#UniqueConstraint(columnNames = {"user_id", "business_id"})
})
public class Favorite {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "appuser_id")
#NotNull
private UUID appuser_id;
#Column(name = "business_id")
#NotNull
private Long businessId;
}
instead of:
#Data
#Entity
#Table(name = "favorite", schema = "public" , uniqueConstraints = {
#UniqueConstraint(columnNames = {"user_id", "business_id"})
})
public class Favorite {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "appuser_id", referencedColumnName="id")
private AppUser appUser;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "business_id", referencedColumnName="id")
private Business business;
}
Problem is, solution #1 has no foreign key ref of any kind, is there a way to do this? I would really prefer my result objects to only have ids and not the entire user / business entities every time.
I know I can make a dto with ids only and manually set it up / workaround, but I'd like an answer to this problem if one exists.

Get only one field from the related table

I have following connection between tables Image:
#Entity
#Table(name = "image")
#Data
public class Image {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private byte[] image;
#OneToOne(mappedBy = "avatar")
private Personal personal;
}
and Personal
#Entity
#Table(name = "personal")
#Data
public class Personal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String position;
private String phone;
private String email;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "avatar_id", referencedColumnName = "id")
private Image avatar;
}
I want to get back from my service Personal entities with ONLY id field from Image table. Repositories and services are standard from tutorials - without extra code or overrides
If read-only is ok use a DTO or interface projection.

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

JPA two Entities one Relationship: How do I obtain a Set of an entity that is linked through a relationship?

I have three tables each mapping to one of these entities. The 'assigned' table acts as the relationship between 'users' and 'roles' with a foreign key to each table. How would I map this on my entities so that I can get a Set of EntityRoles from the UserEntity? I can't quite figure out how to make this work. Is this even possible?
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private long id;
#Column(name="user_username")
private String username;
#Column(name="user_password")
private String password;
#Column(name="user_email")
private String email;
//I want to be able to get a set of RoleEntities
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id")
private Set<RoleEntity> roles;
}
#Entity
#Table(name = "assigned")
public class AssignedEntity implements Serializable {
#Id
//#Column(name = "assigned_role")
#ManyToOne(targetEntity = RoleEntity.class, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_role")
private long roleId;
#Id
//#Column(name = "assigned_user")
#ManyToOne(targetEntity = UserEntity.class, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_user")
private long userId;
}
#Entity
#Table(name = "roles")
public class RoleEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="role_id")
#OneToOne(fetch = FetchType.LAZY, mappedBy="roleId")
private long id;
#Column(name="role_name")
private String name;
}
You are using an incorrect/inconvenient mapping. Always keep things as simply as possible.
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
}
#Entity
#Table(name = "roles")
public class Role {
#Id
private Long id;
#Column
private String name;
}
A persistent provider will create a (valid) join table for you. You can specify the name of the join table using #JoinTable annotation. Also you will need to think about auto generation values of id for the Role entity: the roles table is something like a reference data table. So, probably, you will need to hardcode the id values.
To get user roles (in the persistent context):
user.getRoles()

Can I somehow cascade.PERSIST the map key in a Map<Entity, Entity>?

The situation: I have a class with a Map like this:
#Entity
public class Bar {
#Id
#GeneratedValue
private Long id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "fooMap",
joinColumns = #JoinColumn(name = "One_ID", referencedColumnName = "ID") ,
inverseJoinColumns = #JoinColumn(name = "Two_ID", referencedColumnName = "ID") )
#MapKeyJoinColumn(name = "Bar")
private Map<FooOne, FooTwo> fooMap;
}
with key/value-classes like this (FooTwo looks more or less the same):
#Entity
public class FooOne {
#Id
#GeneratedValue
private Long id;
#Basic
private String name;
If I populate the map and try to persist it, I get the following exception:
UnitOfWork(117839394)--java.lang.IllegalStateException: During synchronization >a new object was found through a relationship that was not marked cascade >PERSIST: jpa.test.minimalExample.FooOne#2e4389ed.
because JPA cascades only to the target of the -toMany association which is the value entity.
Does JPA in general or EclipseLink as implementation offer any form of annotation to cascade actions to the key class of this map? What are my options here ?
I know adding a reference to FooOne in the FooTwo-class works (like this):
#Entity
public class FooTwo {
#Id
#GeneratedValue
private Long id;
#OneToOne(cascade = cascadeType.ALL)
private FooOne foo1;
}
I would rather not add any additional fields to my classes if I can avoid it.