OSIV:false , cannot get session for resolving FetchType.Lazy items, even in the scope of #Transactional - spring

In my spring application, I have disabled osiv (spring.jpa.open-in-view=false) to avoid holding hibernate sessions throughout a request lifecycle.
After disabling OSIV, I am getting the following error when I try to access a field in my entity that has the FetchType set as LAZY
org.hibernate.LazyInitializationException: could not initialize proxy [sg.identity.auth.entity.OrganizationEntity#2] - no Session
This is the entity I'm using and I'm trying to access userEntity.getOrganization() which results in the LazyInitializationException.
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "\"USER\"")
public class UserEntity extends AbstractAuditingEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#OneToOne(fetch = FetchType.LAZY)
#JoinTable(name = "organization_user",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "organization_id", referencedColumnName = "organization_id"))
private OrganizationEntity organization;
#ManyToMany(cascade = CascadeType.MERGE)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "user_parent_organization_unit_mapping",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "organization_unit_id", referencedColumnName = "organization_unit_id"))
private Set<OrganizationUnitEntity> organizationUnits;
#OneToOne(cascade = CascadeType.ALL)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "user_exclusion_mapping",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "excluding_permission_set_id", referencedColumnName = "permission_set_id"))
private PermissionSetEntity exclusionPermissions;
#ManyToMany(cascade = CascadeType.MERGE)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "role_id"))
private Set<RoleEntity> roles;
}
I'm calling the user.getOrganizationEntity in a method annotated with #Transactional so It should receive an open session to resolve the lazy items. But in my case, it is not working
#Transactional
public Organization getOrganization(String emailId) {
return userRepository.getUserByEmailId(emailId)
.map(UserEntity::getOrganization)
.map(OrganizationMapper::toDto)
.orElseThrow(() -> new IllegalArgumentException("error"));
}
The user.getOrganization is resolving correctly when I try using TransactionTemplate.
#Transactional
public Organization getOrganization(String emailId) {
return transactionTemplate.execute(status -> {
return userRepository.getUserByEmailId(emailId)
.map(UserEntity::getOrganization)
.map(OrganizationMapper::toDto)
.orElseThrow(() -> new IllegalArgumentException("error"));
});
}
I got it working with #Transactional itself by setting the property spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true, but from my research, i found that it is not recommended to set this flag as true

Related

Springboot ExampleMatcher always returns no result

I have the following entity and I'm trying to use ExampleMatcher for simple queries:
#Entity(name="UserAccount")
#Table(name = "useraccount", catalog = "useraccount")
#Data
public class UserAccount implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false, unique = true)
private String mail;
private String password;
private boolean isEnabled;
private Timestamp credentialExpire;
private boolean isAccountNonLocked;
private boolean isSuspended;
private Timestamp accountExpire;
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#JoinTable(name = "user_to_privileges", catalog = "useraccount",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "privilege_id", referencedColumnName = "id", nullable = false)})
private Set<Privilege> privileges= new HashSet<>();
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#JoinTable(name = "user_to_organizations", catalog = "useraccount",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "organization_id", referencedColumnName = "id", nullable = false)})
private Set<Organization> organizations= new HashSet<>();
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
#Setter(AccessLevel.NONE)
private UserRegister register;
#OneToMany(mappedBy ="tokenId.user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
private Set<SecureToken> tokens = new HashSet<>();
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
private UserLogin login;
//all the methods omitted from brevity
}
I create the Example matcher as follows:
UserAccount account= new UserAccount();
account.setUsername("John");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example<UserAccount> regExample = Example.of(account, matcher);
List<UserAccount> out = repository.findAll(regExample);
Consider that a user with username "John" exists, but the output is always empty, no matter what parameter I fill.
Edit: this helps to find the solution: Are there any possible ways to ignore all paths of JPA Example Matcher. There is no way to automatically ignore primitive fields when not used?
Edit: Notice that I want to find all the UserAccount containing the specified strings in the selected fields. With other entities the configuration of ExampleMatcher works.

Application of ManyToMany - Spring does not work

I am implementing many to many relationship between Users and Permissions and I cannot conclude where am I wrong, it is not working (Unable to map collection rs.raf.demo.model.User.permissions):
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column
private String username;
#Column
private String password;
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "ID")
)
private List<Permission> permissions = new ArrayList<>();
}
=================================================================================
#Data
#Entity
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "permission_id")
private Long userId;
private String type;
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID")
)
#JsonIgnore
private List<User> users = new ArrayList<>();
}
I implemented Users and Permissions with looking in this example, this is working (and I cannot see difference with Users and Permission):
#Data
#Entity
#Table(name = "STUD")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String lastName;
private String firstName;
#ManyToMany
#JoinTable(
name = "STUDENTS_COURSES",
joinColumns = #JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "COURSE_ID", referencedColumnName = "ID")
)
private List<Course> courses = new ArrayList<>();
}
================================================================================
#Data
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
#ManyToMany
#JoinTable(
name = "STUDENTS_COURSES",
joinColumns = #JoinColumn(name = "COURSE_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID")
)
#JsonIgnore
private List<Student> students = new ArrayList<>();
public void addStudent(Student student) {
students.add(student);
student.getCourses().add(this);
}
public void removeStudent(Student student) {
students.remove(student);
student.getCourses().remove(this);
}
}
JPA uses (and creates in your case probably) intermediate join table for many to many relationship and you cannot use different reference column name from name you specified in entity.
So you need to set:
in User class
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "PERMISSION_ID")
)
in Permission
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "PERMISSION_ID"),
inverseJoinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
)
And same for other tables.

Delete just one side of a manytomany relationship Hibernate

I have two tables that have a manytomany relationship:
first one is ad ( represents all the products)
#Entity
#Table(name = "ad")
public class Ad {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "admin_id")
private Admin admin;
#ManyToMany(mappedBy = "ads", fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
Second one is order:
#Entity
#Table(name = "`order`")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne( cascade=CascadeType.
#JoinColumn(name = "buyer_id")
private Buyer buyer;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "order_ad", joinColumns = {
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "ad_id", referencedColumnName = "id", nullable = false, updatable = false) })
private List<Ad> ads = new ArrayList<>();
when I delete order using its repository that is representing a cancellation so I don't want the ads to be deleted as well.
How can I do that?
PS: I can't find a replacement for the orphanRemoval of the onetomany relationship

object references an unsaved transient instance - Spring, JPA Hibernate

Here is the code:
#Entity
public class PortalUser {
#NotNull
#OneToMany(mappedBy = "portalUser", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<PortalUserOrganisation> portalUserOrganisations;
#NotNull
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "portalUser", orphanRemoval = true)
private Set<UserRole> userRoles = new HashSet<UserRole>();
}
#Entity
public class PortalUserOrganisation {
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private PortalUser portalUser;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ORGANISATION_ID", referencedColumnName = "ID")
private Organisation organisation;
}
#Entity
public class Organisation {
#OneToMany(mappedBy = "organisation", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Set<PortalUserOrganisation> portalUserOrganisations;
}
#Entity
public class UserRole {
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private PortalUser portalUser;
#NotNull
#ManyToOne(fetch = FetchType.LAZY, optional=true)
#JoinColumn(name = "ROLE_ID", referencedColumnName = "ID")
private RoleLookup roleLookup;
}
#Entity
public class RoleLookup extends AbstractLookupEntity {
#OneToMany(mappedBy = "roleLookup", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Set<UserRole> userRoles = new HashSet<UserRole>();
}
Code to Create a User:
#Transactional
saveUser(userObj)
PortalUser portalUser = new PortalUser;
portalUser.setStatus(status);
PortalUserOrganisation userOrganisation = null;
for (OrganisationsDto dto : organisationsList()) {
userOrganisation = new PortalUserOrganisation();
userOrganisation.setOrganisation(organisationRepository.findOne(dto.getId()));
userOrganisation.setPortalUser(portalUser);
userOrganisation.setCreatedUpdatedBy(context.getName());
userOrganisation.setCreatedUpdatedDate(createUpdateDate);
userOrganisation.setAction(portalUser.getAction());
userOrganisation.setStatus(portalUser.getStatus());
userOrganisation.setActive(true);
portalUser.getPortalUserOrganisation().add(userOrganisation);
}
UserRole userRole = null;
for (RoleLookupDto dto : portalUserDto.getUserRoles()) {
userRole = new UserRole();
userRole.setPortalUser(portalUser);
userRole.setRoleLookup(roleLookupRepository.findOne(dto.getId()));
userRole.setCreatedUpdatedBy(context.getName());
userRole.setCreatedUpdatedDate(createUpdateDate);
userRole.setAction(portalUser.getAction());
userRole.setStatus(portalUser.getStatus());
userRole.setActive(true);
portalUser.getUserRole().add(userRole);
}
portalUser.setActive(false);
portalUser = portalUserRepository.save(portalUser);
return portalUser;
I have see so many post, but this has not solved my issue. Any help is appreciated. Here the RoleLookup is a static table. Here is the exception:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.commerzbank.clearing.ccp.domain.UserRole.roleLookup -> com.commerzbank.clearing.ccp.domain.RoleLookup; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.commerzbank.clearing.ccp.domain.UserRole.roleLookup -> com.commerzbank.clearing.ccp.domain.RoleLookup
You should set a cascade = "save-update " for many-to-one side.

Hibernate - map a row to either of two subclasses

I have a Superclass
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "entity_type", discriminatorType = DiscriminatorType.STRING)
#Table(name = "my_super")
public abstract class MySuper{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "super_id")
private Long id;
}
and two subclasses
#Entity
#DiscriminatorValue("sub1")
public class Sub1 extends MySuper {}
#Entity
#DiscriminatorValue("sub2")
public class Sub2 extends MySuper {}
Now if another class has both of these subclasses in it, is it possible to instantiate one of them through the same join table - same row ??
For instance:
#Entity
#Table(name = "caller")
public class Caller{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caller_id")
Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Set<Sub1> sub1s;
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
Sub2 sub2;
}
I keep getting this error when trying to instantiate a Caller Object:
org.springframework.orm.hibernate3.HibernateSystemException: could not set a field value by reflection setter of my.test.Caller.sub2; nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter of my.test.Caller.sub2
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:679)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:102)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:368)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter
i think this two line shouldnt be same
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Set<Sub1> sub1s;
--
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Sub2 sub2;
because they are not same entity and their join table should be different. For ManyToMany relationship type is Sub1 but if you try to put them same table hibernate will try to put Sub2 to sub1s . But it is not vaild. Try to change your join table. For ManyToOne relationship.

Resources