Best way to map and query this entities - java

I have three entities User, Center and Profile, so a user has different profiles depending on the center he's in. So we have a table which links the three entities called *user_center_profile*.
Then we have another entity, called Permission which is linked to Profile, so a profile has several permissions.
Because the main entity is User, I have it maped like this:
#Entity
#Table(name = "profile")
public class Profile implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idprofile")
private Long idProfile;
#Column(name = "codprofile")
private String codProfile;
#Column(name = "desprofile")
private String desProfile;
#OneToMany
#JoinTable(name = "profile_permission", joinColumns = { #JoinColumn(name = "idProfile") }, inverseJoinColumns = { #JoinColumn(name = "idPermission") })
private List<Permission> permissions = new ArrayList<Permission>();
/* getters and setters */
}
#Entity
#IdClass(CenterProfileId.class)
#Table(name = "user_center_profile")
public class CenterProfile implements Serializable {
#Id
#ManyToOne
private Center center;
#Id
#ManyToOne
private Profile profile;
/* getters and setters */
}
#Embeddable
public class CenterProfileId implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "idCenter")
private Center center;
#ManyToOne
#JoinColumn(name = "idProfile")
private Profile profile;
/* getters, setters, equals, hashcode */
}
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idUser")
private Long idUser;
#Column(name = "codUser")
private String codUser;
/* other properties */
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_center_profile", joinColumns = { #JoinColumn(name = "idUser") }, inverseJoinColumns = {
#JoinColumn(name = "idCenter"), #JoinColumn(name = "idProfile") })
private List<CenterProfile> centersProfileUser = new ArrayList<CenterProfile>();
#Transient
private Center selectedCenter;
/* getters, setters */
}
The thing is that at certain point I have to collect all the permissions that a certain user has... I tried several things and I got lazy load errors, no session or session closed errors, problem loading simultaneous bags...
I even tried to write a plain SQL query and I got the same error...
I can't see the way to build a DetachedCriteria for this, and I don't know if it will give an exception too..
My app can connect to different "centers", when he logs in he can choose which center he wants to connect to, but also once logged in, he can change centers... So when he changes it, I have to recalculate his permissions... that's why I need to get that list of permissions..
How could I get this done in a proper way?

In the end, I built the query like this:
HashSet<Permission> set = new HashSet<Permission>();
for (CenterProfile cp : usuario.getCentersProfileUsuario()) {
// First we build subcriteria to return all the permissions for a certain profile
DetachedCriteria criteriaProfile = DetachedCriteria
.forClass(Profile.class);
criteriaProfile.add(Restrictions.eq("idProfile", cp.getProfile()
.getIdProfile()));
criteriaProfile.createAlias("permissions", "permission");
criteriaProfile.setProjection(Property.forName("permission.idPermission"));
// Then we build criteria for permissions, which should match the results given by subcriteria
DetachedCriteria criteria = DetachedCriteria
.forClass(Permission.class);
criteria.add(Property.forName("idPermission").in(criteriaProfile));
List<Permission> permissions = getHibernateTemplate().findByCriteria(
criteria);
set.addAll(permissions);
}

Related

JPA/Hibernate :sql error 1364 sqlstate hy000 hibernate

I am working on a relationship in spring jpa and facing this issue. I am trying to establish relationship between a subuseraccess table and subuservehicleaccess table.
Now a subuser can have access to multiple vehicles and each vehicle will have a set of access privileges for the sub user.(eg: read,write).
The private List<SubUserVehicleAccessFeatureList> property in the first class defines the list of the relation class added later viz SubUserVehicleAccessFeatureList.
Error
I am trying to insert record in these tables and, while I am getting an error sql error 1364 sqlstate hy000 hibernate saying the field subUserAccessFeatureId doesn't have a default value.
I tried to debug and the data while saving is all correct. On the contrary if I remove the #JoinColumn(name = "SUBUSERACCESSFEATUREID")) annotation from the below property private List<SubUserVehicleAccessFeatureList> it works as expected. As a part of convention, we need to have names without underscores and hibernate generates underscored names by default.
Referred this but didn't find a solution.
#Entity
#Table(name = "SUBUSERACCESSFEATURELIST")
public class SubUserAccessFeatureList extends TableMetaDataDTO {
/** The sub user access feature id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SUBUSERACCESSFEATUREID")
private Long subUserAccessFeatureId;
/** The sub user id. */
#Column(name = "SUBUSERID")
private Long subUserId;
/** The vehicle id. */
#Column(name = "VEHICLEID")
private Long vehicleId;
/** The vehicle access feature list. */
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "SUBUSERVEHICLEACCESSFEATURELIST", joinColumns = #JoinColumn(name =
"SUBUSERACCESSFEATUREID"))
private List<SubUserVehicleAccessFeatureList> vehicleAcccessFeatureList = new ArrayList<SubUserVehicleAccessFeatureList>();
/** The account access feature list. */
#Column(name = "FEATUREID")
#JoinTable(name = "SUBUSERACCOUNTACCESSFEATURELIST", joinColumns = #JoinColumn(name = "SUBUSERID"))
#ElementCollection(fetch = FetchType.LAZY)
private Set<Long> accountAccessFeatureList = new HashSet<Long>();
//Getter setter eliminated
}
The relation class
#Entity
#Table(name = "SUBUSERVEHICLEACCESSFEATURELIST")
public class SubUserVehicleAccessFeatureList {
/** The sub user vehicle feature id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SUBUSERVEHICLERELATIONID")
private Long subUserVehicleFeatureId;
/** The vehicle id. */
#Column(name = "VEHICLEID")
private Long vehicleId;
/** The feature id. */
#Column(name = "FEATUREID")
#JoinTable(name = "SUBUSERVEHICLERELATIONTABLE", joinColumns = #JoinColumn(name = "SUBUSERVEHICLERELATIONID"))
#ElementCollection(fetch = FetchType.LAZY)
private Set<Long> featureId = new HashSet<Long>();
}

How to create Hibernate mapping for the following scenario

How to create Hibernate mapping for the following scenario.
I have three tables, Users, Systems and System Assign.
There are three types of user -- Normal user, super user and admin.
Users Table has five columns -- user-ID , userName, password, email id and userType. userType will decide whether a user is super user or admin or normal user.
Systems table have some column but the most important is systemsID which will be used for mapping.
I have created one table System Assign to assign a system to a user with two columns user-ID and system_id (or it can be corrected if required). Also created these two as the foreign key of respective table.
The conditions of mapping are :
a user can have one or more system id on his name.
a system id can be assign to one or more users.
when a system_assign record is deleted from the UI it should only break the link between the user and system but user and system should be in database.
Also I have to make some database changes like this:
If a super user creates a user , this user will be under him
if a super user creates a system, system will be under him.
if a user is deleted then system should come under super user now.
I need to know how to create hibernate classes for this senario
#Entity
#Table(name = "SYSTEM_ASSIGN")
public class SAssign implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1945028654484806943L;
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USERSSO")
private Users user;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "SYSTEMID")
private Systems system;
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public Systems getSystem() {
return system;
}
public void setSystem(Systems system) {
this.system = system;
}
}
I use a list of Role instead of userType and the #ManyToMany association for user and roles. Join tables for the #ManyToMany association will be created by Hibernate. You don't need to use persistent classes for it.
Classes with the mapping: usersystem
A test for mapping: UserSystemTest.java
public enum RoleEnum {
ADMIN, USER, SUPER_USER
}
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue
#Column(name = "f_pid")
private Long pid;
#Column(name = "f_role")
#Enumerated(EnumType.STRING)
private RoleEnum role;
}
#Entity
#Table(name = "systems")
public class SomeSystem {
#Id
#GeneratedValue
#Column(name = "f_pid")
private Long pid;
#Column(name = "f_name")
private String name;
}
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
#Column(name = "f_pid")
private Long pid;
#Column(name = "f_user_name")
private String userName;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "user_roles", joinColumns = { #JoinColumn(name = "fk_user") },
inverseJoinColumns = { #JoinColumn(name = "fk_role") })
private List<Role> roles = new ArrayList<Role>();
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "system_assign", joinColumns = { #JoinColumn(name = "fk_user") },
inverseJoinColumns = { #JoinColumn(name = "fk_system") })
private List<SomeSystem> systems = new ArrayList<SomeSystem>();
}

JPA refresh entity

I have two tables in my database USERS and ADDRESSES, each user can have many addresses.
I have build entity classes with NetBeans wizard, and it create the classes well:
#Entity
#Table(name = "USERS")
#XmlRootElement
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
// Some fields.......
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Collection<Address> addressCollection;
public User() {
}
// Getters and Setters.......
}
#Entity
#Table(name = "ADDRESSES")
#XmlRootElement
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected AddressPK addressPK;
// Some fields.......
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", insertable = false, updatable = false)
#ManyToOne(optional = false)
private User user;
public Address() {
}
// Getters and Setters.......
}
#Embeddable
public class AddressPK implements Serializable {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private int id;
#Basic(optional = false)
#NotNull
#Column(name = "USER_ID")
private int userId;
public AddressPK() {
}
}
In the session I save the User instance of the current logged in user. But the User's addressCollection never updates when I change the database like:
Address newAddr = new Address();
// Sets values to newAddr......
AddressFacade addrDao = new AddressFacade();
addrDao.create(newAddr); // Succeeded
LoginManager.getCurrentUser().getAddressCollection(); // Returns the old address list.
How can I refresh the current user's instance to get the correct addressCollection?
First, when you have a bidirectional relationship, JPA requires that you keep both sides of the relationship in synch with each other. This allows caching entities and other performance enhancements to be enabled by many providers. In this case, when you set the USER_ID field, you should update the User's addressCollection that is affected by the change so that your object model stays in synch with what you are committing to the database.
An alternative is to force a refresh manually on the User instance. This can be done with a em.refresh(user) call, or through provider specific options and query hints. This is usually the least performant option though as it requires a database hit that isn't needed.

JPA Query from 3 column join table

I have a following Entities. Means that User can belong to many businesses and for each business this user can has separate permissions. So an existing user can be assigned to another business. The Business_User table looks like this:
USER_ID BUSINESS_ID AUTHORITY_NAME
6 1 ROLE_ANALYTICS
6 1 ROLE_SELF_ANALYTICS
7 1 ROLE_REVIEWS
8 1 ROLE_ANALYTICS
8 1 ROLE_SELF_ANALYTICS
8 1 ROLE_REVIEWS
6 2 ROLE_REVIEWS
6 2 ROLE_SELF_ANALYTICS
Question: I am querying Users for ONE business by trying to build list of user DTO objects, that DTO exposes List<Authority>, problem is that I can't figure how should I get these authorities for each user from the Business_User table. Been trying to do fancy stuff with lambdas but have no luck. I am using Spring Data for queries, maybe can solve it in the repository.
Thanks in advance.
EDIT: If I would go another route by adding new join table BUSINESS_USER_AUTHORITY, how would I have to describe it in UserBusiness class? I also would like the primary key to be over user_id and business_id. Note that name is PK in Authority table.
Something like this, but that does not create me join table at all.
Change UserBusiness class:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "BUSINESS_USER_AUTHORITY",
joinColumns = {#JoinColumn(name = "business_user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "authority_name", referencedColumnName = "name")})
private Set<Authority> authorities = new HashSet<>();
User
#Entity
#Table(name = "USER")
public class User extends AbstractAuditingEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "user_id", referencedColumnName = "id")
})
private Set<BusinessUser> businessUsers = new HashSet<>();
}
BusinessUser
#Entity
#Table(name = "BUSINESS_USER")
#IdClass(BusinessUser.class)
public class BusinessUser implements Serializable {
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "business_id")
private Business business;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "authority_name")
private Authority authority;
}
Business
#Entity
#Table(name = "BUSINESS")
public class Business implements Serializable {
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "business_id", referencedColumnName = "id")
})
private Set<BusinessUser> businessUsers = new HashSet<>();
}
Authority
#Entity
#Table(name = "AUTHORITY")
public class Authority implements Serializable {
#NotNull
#Size(min = 0, max = 50)
#Id
#Column(length = 50)
private String name;
}
I ended up writing a function like that, but if anyone has a solution how would the design look with another join table between BusinessUser <--> BusinessUserAuthority then I'd be glad to implement it.
private Function<Map.Entry<User, List<BusinessUser>>, UserManagementDTO> getPermissionForUser() {
return user -> {
UserManagementDTO dto = userManagementMapper.userToUserManagementDTO(user.getKey());
dto.setAuthorities(user.getValue().stream().map(BusinessUser::getAuthority).collect(Collectors.toSet()));
return dto;
};
}

hibernate one to many single direction

I'm trying to make some hibernate stuff and create sql scripts automatically based on the hibernate annotations. Here is what I have:
Schedule class:
#Entity
#Table(name = ScheduleTableConsts.TABLE_NAME)
public class Schedule implements Serializable {
#Id
#Column(name = ScheduleTableConsts.ID_COLUMN)
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = ScheduleSlotTableConsts.ID_COLUMN)
private List<ScheduleSlot> scheduleSlots;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = LessonTableConsts.ID_COLUMN)
private List<Lesson> lessons;
Constructors, getters, setters...
ScheduleSlot class:
#Entity
#Table(name = ScheduleSlotTableConsts.TABLE_NAME,
uniqueConstraints = {#UniqueConstraint(columnNames = {TimeSlotTableConsts.ID_COLUMN,
PlaceSlotTableConsts.ID_COLUMN})})
public class ScheduleSlot implements Serializable {
#Id
#Column(name = ScheduleSlotTableConsts.ID_COLUMN)
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne
#JoinColumn(name = TimeSlotTableConsts.ID_COLUMN)
private TimeSlot timeSlot;
#OneToOne
#JoinColumn(name = PlaceSlotTableConsts.ID_COLUMN)
private PlaceSlot placeSlot;
Constructors, getters, setters...
Lesson class:
#Entity
#Table(name = LessonTableConsts.TABLE_NAME)
public class Lesson implements Serializable {
#Id
#Column(name = LessonTableConsts.ID_COLUMN)
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(fetch = FetchType.LAZY)
private Set<Professor> professors;
#OneToMany(fetch = FetchType.LAZY)
private Set<Course> courses;
#OneToMany(fetch = FetchType.LAZY)
private Set<Group> groups;
#Column(name = LessonTableConsts.LAB_COLUMN)
private boolean lab;
Constructors, getters, setters...
What I'm trying to achieve is to let schedule know about it's slots and lessons and not to let slots and lessons know about the schedule they are belong to. The code above seems to be ok, but when I'm trying to generate sql script (using maven and hibernate3-maven-plugin for that purposes) and run it the following happens:
It creates a SCHEDULE table with no pointers to SCHEDULESLOT or LESSON tables;
It creates SCHEDULESLOT and LESSON tables with no pointers to SCHEDULE table.
Could somebody, please, tell me what I am doing wrong?
Thank in advance!
There is a thing you are forgetting to notice:
When using #OneToMany annotation, you cannot simply use #JoinColumn. However, on the other side, I mean #ManyToOne: you can use #JoinColumn.
It should be clear to you why this works this way, and not the other way around. (How can an entity keep so many IDs in just one column?)
So you have two options here:
1) Use a specific table to store relation between your One and Many entities. Something like this:
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name = "SCHEDULE_SLOTS_MAPPING", joinColumns = #JoinColumn(name = "SCHEDULE_ID"), inverseJoinColumns = #JoinColumn(name = "SCHEDULE_SLOT_ID"))
private List<ScheduleSlot> scheduleSlots;
2) Use the #JoinColumn on the Many side of the relationship: (e.g. your ScheduleSlot class)
#ManyToOne
#JoinColumn(name="SCHEDULE_ID")
private Schedule schedule;

Categories

Resources