QueryDsl BooleanExpression - nested field is NULL - spring-boot

I want to create a predicate with QueryDSL and I don't know why but some nested field is NULL:
BooleanExpression emailSender = qFreight.message.account.user.id.eq(userId);
user is NULL.
qFreight.message.account.user gives me NPE error
And then in logs I have
.180 ERROR 7316 --- [nio-9090-exec-3] p.a.m.s.filter.JwtAuthorizationFilter : Request processing failed; nested exception is java.lang.NullPointerException
Of course I have generated QFreight, QMessage, QAccount, QUser... Where can bet the problem? I don't have any idea
UPDATE
My entities (the question is how to set #QueryInit properly):
#Entity
#Table(name = "freight")
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Freight {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#QueryInit("*")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "message_id")
private Message message;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
#Entity
#Table(name="message")
#Getter
#Setter
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "email_account_id", foreignKey = #ForeignKey(name = "FK_account_to_message"))
private Account account;
}
#Entity
#Table(name="account")
#Getter
#Setter
#NoArgsConstructor
#SuperBuilder(builderMethodName = "of")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
}

qFreight.message.account.user.id is not a valid JPQL expression. You'd have to create the joins between the entities explicitly: JOIN qFreight.message message JOIN message.account account JOIN account.user user. In QueryDSL this would be: .join(qFreight.message, QMessage.message).join(QMessage.message.account, QAccount.account).join(QAccount.account.user, QUser.user).where(QUser.user.id.eq(...)).
Furthermore, the QueryDSL metamodel by default is only initialized one level deep. This is a performance decision. If you need the metamodel to be initialized deeper (even though usecases are rare, because JPQL lacks support for implicit joins), you have to use #QueryInits: http://www.querydsl.com/static/querydsl/4.4.0/reference/html_single/#d0e2265

Related

#ManyToOne and #OneToMany ends up in unlimited loop when retrieved through profileRepository.getByProfileId(id);

Class Jobs has Many to One relationship with Profile.
When I retrieve through profileRepository.getByProfileId(id) the response returns recursive data.
Also if you notice Profile has Login object. I don't want to return that as well.
#Entity
#Table(name = "tbl_profile")
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#Getter
public class Profile {
#Id
#Column(name = "profile_id")
#GeneratedValue(strategy = GenerationType.AUTO)
long profileId;
#NonNull
#Column(name = "name")
String name;
#Column(name = "description", nullable = false)
String description;
#OneToOne
#JoinColumn(name = "login_id",
referencedColumnName = "login_id")
Login login;
#OneToMany(
mappedBy = "profile"
)
List<Jobs> job;
Class Jobs
#Entity
#Table(name = "tbl_job")
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#Getter
public class Jobs {
#Id
#Column(name = "job_id")
#GeneratedValue(strategy = GenerationType.AUTO)
long jobId;
#NonNull
#Column(name = "job_role", nullable = false)
String joRole;
#Column(name = "description", nullable = false)
String description;
#ManyToOne
#JoinColumn(name = "profile_id",
referencedColumnName = "profile_id")
Profile profile;
}
Use #JsonIgnore to the property to ignore the output on JSON. Also according to your business logic, recheck if you need bidirectional association. You could maybe add only unidirectional association.

JPA Repository.findByKeyEquals() returns not present value, but the value is exist on db

I'm developing an application that queries a database.
There are a few issues right now.
history.isPresent() == false when calling Optional<History> findByKeyEquals() intermittently. but value is exist on database
This is the information I got while tracking the issue.
All child entities are non-null.
In most cases, if the same function is re-executed, it is searched.
But sometimes it doesn't happen intermittently.
i think that i use incorrectly table relationship annotation (#OneToMany,#ManyToOne options..)
I want to solve this issue.
this is my code
History (Parent)
#Table(
indexes = {
#Index(columnList = "key", unique = true),
})
#Entity
#Getter
#ToString
#Audited
public class History implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(nullable = false, columnDefinition = "CHAR(36)")
private String key = UUID.randomUUID().toString();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date started = new Date();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date finished;
#Setter
#OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "history",
orphanRemoval = true)
#NotAudited
private List<Content> contents = new ArrayList<>();
...
}
Content (Child)
#Table
#Entity
#Getter
#Audited
public class Content implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(columnDefinition = "LONGTEXT")
#NotAudited
private String content;
#Setter
#ManyToOne(targetEntity = History.class, fetch = FetchType.Lazy, optional = false)
#Audited
private History history;
...
}
Repository
public interface HistoryRepository
extends JpaRepository<History, Long>, RevisionRepository<History, Long, Integer> {
Optional<History> findByKeyEquals(final String key);
}

Infinite JSON in ManyToMany relationship mapped by Intermediary Table

I have 2 entities that relate to one another. These 2 entities should map to each other in a Many-To-Many relationship, however, I need to also have a timestamp of their respective relationship (when it happened), so I am trying to map them using an intermediary table.
Initially, the relationship was One-To-Many, but I realized that I actually need a Many-To-Many as the business logic requires this. The structure is still the same, as in there is a Parent-Child relationship, but this time, a child should have multiple parents as well.
My BaseEntity is an abstract class that contains the fields present in all the other entities:
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Id
#Min(100)
#Max(Integer.MAX_VALUE)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#CreationTimestamp
#Column(name = "Created_At", updatable = false)
protected ZonedDateTime createdDate;
#UpdateTimestamp
#Column(name = "Updated_At")
protected ZonedDateTime updatedDate;
#NotNull
#Column(name = "Is_Active")
protected Boolean active = true;
}
Then I have my 2 entities that should relate in a Many-To-Many style. This is my first entity and should be the parent:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "User")
#EqualsAndHashCode(callSuper = true)
#TypeDefs( {
#TypeDef(name = "json", typeClass = JsonStringType.class),
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class UserEntity extends BaseEntity {
#NotBlank
#Column(name = "User_Name", columnDefinition = "varchar(255) default 'N/A'")
private String userName;
#Nullable
#JoinColumn(name = "User_Id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<UserRole> roleList = new ArrayList<>();
}
My second entity is considered the child entity:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "Role")
#Where(clause = "is_active = true")
#EqualsAndHashCode(callSuper = true)
public class RoleEntity extends BaseEntity {
#NotBlank
#Column(name = "Name")
private String name;
#JsonIgnore
#JoinColumn(name = "Role_Id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<UserRole> userList = new ArrayList<>();
}
I also have my intermediary entity:
#Data
#Entity
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Where(clause = "is_active = true")
#EqualsAndHashCode(callSuper = true)
#Table(name = "User_Role", uniqueConstraints= #UniqueConstraint(columnNames={"User_Id", "Role_Id"}))
public class UserRole extends BaseEntity {
// Adding #JsonIgnore here will only cause an error
#JoinColumn(name = "User_Id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, targetEntity = UserEntity.class)
private UserEntity user;
#JoinColumn(name = "Role_Id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, targetEntity = RoleEntity.class)
private RoleEntity role;
}
Problem now is that when I try to get my UserEntity, I get infinite recursion.
So far I've tried using #JsonIgnore, #JsonManagedReference, #JsonBackReference and it did not work or I simply don't know where or how to use them properly.
Recap:
2 entities mapped by Many-To-Many relationship;
Many-To-Many implemented using an intermediary entity and One-To-Many + Many-To-One associations;
Getting recursion when showing my UserEntity;
Update: I managed to get this fixed using a different approach described in my answer to this question.
I fixed this by implementing a Composite Key structure and just using the #JsonIgnore annotation:
#Getter
#Setter
#Embeddable
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor
public class UserRoleKey implements Serializable {
#Column(name = "User_Id")
Long userId;
#Column(name = "Role_Id")
Long roleId;
}
This gets to be used in the intermediary entity, which now doesn't use my BaseEntity anymore.
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "User_Role", uniqueConstraints= #UniqueConstraint(columnNames={"User_Id", "Role_Id"}))
public class UserRole {
#JsonIgnore
#EmbeddedId
private UserRoleKey id;
#JsonIgnore
#MapsId("userId")
#JoinColumn(name = "User_Id")
#ManyToOne(optional = false, targetEntity = UserEntity.class)
private UserEntity user;
#MapsId("roleId")
#JoinColumn(name = "Role_Id")
#ManyToOne(optional = false, targetEntity = RoleEntity.class)
private RoleEntity role;
#CreationTimestamp
#Column(name = "Created_At", updatable = false)
private ZonedDateTime createdDate;
}
Now, for my two entities, I have this definition:
UserEntity class (definition of the role):
#Nullable
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = true)
private List<UserRole> roleList = new ArrayList<>();
RoleEntity class (definition of the user)
#Nullable
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "role", orphanRemoval = true)
private List<UserRole> userList = new ArrayList<>();
This seems to be working and no longer returns an infinite JSON recursion.

Spring Framework + Spring Data + Hibernate Jpa OneToMany child removal fails

I have an unidirectional OneToMany JPA entity mapping in my (Spring Framework + Spring Data + Hibernate JPA) project. Entity classes are like in the following code.(I have removed irrelevant class members for brevity).
#Entity
#Table(name = "employees")
class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "employee_id")
private List<DepartmentAssignment> departmentAssignments = new ArrayList<>();
}
#Entity
#Table(name = "department_assignments")
class DepartmentAssignment{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#NotNull
#Column(name = "employee_id")
private Integer employeeId;
#NotNull
#Column(name = "department_id")
private Integer departmentId;
}
#Entity
#Table(name = "departments")
class Department{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
}
And, in one of my service classes have a method to remove a DepartmentAssignment from an Employee like below.
public Employee deleteDepartmentAssignment(Integer empId, Integer deptAssignmentId) {
Employee employee = employeeRepository.findOne(empId);
if(employee != null) {
for ( DepartmentAssignment da : employee.getDepartmentAssignments()) {
if(da.getId().equals(deptAssignmentId)) {
employee.getDepartmentAssignments().remove(da);
employee = employeeRepository.save(employee);
break;
}
}
}
return employee;
}
However, calling above methods gives me an error: org.hibernate.exception.ConstraintViolationException ,and in the SQL log, I can see Column 'employee_id' cannot be null error for the last SQL statement of the transaction.
Can anybody tell me what I'm doing wrong here and how to get it fixed?
You don't need to add
#NotNull
#Column(name = "employee_id")
private Integer employeeId;
to the Employee, if you use #JoinColumn(name = "employee_id"). Try to remove it.
You can try the following, not sure why you use the plain id in the object. Thats not object relational mapping.
For more details see Hibernate triggering constraint violations using orphanRemoval
#Entity
#Table(name = "employees")
class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee", orphanRemoval = true)
private List<DepartmentAssignment> departmentAssignments = new ArrayList<>();
}
#Entity
#Table(name = "department_assignments")
class DepartmentAssignment{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(optional=false)
private Employee employee;
#ManyToOne(optional=false)
private Department department;
}
#Entity
#Table(name = "departments")
class Department{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
You must look .hbm.xml file and you should mapping your Entity in this file and
you can look this example
http://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example/
I hope it will be useful for you.
try removing
cascade = CascadeType.ALL
but im not 100% sure..

Spring mapping using hibernate

I am joining two table using one to many relationship
my first table is role and second is roleCompany. where role_id is reference key in roleCompany table.
But when I am writting query to get all companies for specific id it showing me following error
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/myproject] threw exception [Request processing failed; nested exception is org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.test.myproject.domain.entity.RoleEntity.id] with root cause
java.lang.IllegalArgumentException: Can not set java.lang.Long field com.test.myproject.domain.entity.RoleEntity.id to java.lang.Long
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
My RoleEntity Class is :
#Entity
#Table(name = "roles")
public class RoleEntity {
#Id
#Getter
#Setter
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#Getter
#Setter
#Column(name = "role", nullable = false)
private String role;
#Getter
#Setter
#OneToMany(mappedBy = "roles")
List<RoleCompanyEntity> companyentities;
}
and my RoleCompanyEntity is
#NamedQueries({
#NamedQuery(name = RoleCompanyEntity.FIND_ROLE_COMPANY_BY_ROLE_ID, query = "select r.companyId, r.companyName from RoleCompanyEntity r where r.roles =:roleId")
})
#Entity
#Table(name = "role_company")
public class RoleCompanyEntity {
public static final String FIND_ROLE_COMPANY_BY_ROLE_ID = "findRoleCompanyByRoleId";
#Id
#Getter
#Setter
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "company_id", unique = true, nullable = false)
private Long companyId;
#Getter
#Setter
#Column(name = "company_name", unique = true, nullable = false)
private String companyName;
#Getter
#Setter
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="role_id", nullable=false)
private RoleEntity roles;
}

Resources