Duplicate parents after upgrading to Spring boot 3.2.x(hibernate 6) while using entity graph

29 views Asked by At

I have a User entity which has one many to many relationship with Authority table and many to one with UserGroup and another many to one with company.

I am using a Projection Interface named UserDto and trying to fetch all users with the help of entity graph with attributePaths as authorities, company and userGroup but before spring 3 it always gave me distinct parents but now it is doing cartesian product.

I read somewhere that Hibernate 6 automatically de-duplicates the result set. So i tried writing a jpa query by myself using join fetch and adding distinct key word and used User entity instead of project but same result.

@Entity
@Table(name = "USERS")
public class User extends Cacheable<Integer, User> implements AuditableEntity {

private static final long serialVersionUID = -4759265801462008942L;

@Id
@Column(name = "USER_ID", nullable = false)
@TableGenerator(name = "USER_ID", table = "ID_GENERATOR", pkColumnName = "GEN_KEY", valueColumnName = "GEN_VALUE",
    pkColumnValue = "USER_ID", allocationSize = 10)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "USER_ID")
private Integer id;

@Column(name = "EMAIL_ID", unique = true, nullable = false, length = 254)
private String emailId;

@JsonIgnore
@Column(name = "PASSWORD", nullable = false, length = 60)
private String password;

@Column(name = "ENABLED", nullable = false)
private boolean enabled = false;

@Column(name = "LOCKED", nullable = false)
private boolean locked = false;

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
    name = "USER_AUTHORITY",
    joinColumns = {@JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
    inverseJoinColumns = {@JoinColumn(name = "AUTHORITY_NAME", referencedColumnName = "NAME")})
@NotNull
private Set<Authority> authorities = new HashSet<>();

@NotEmpty
@Size(max = 60)
@Column(name = "FIRST_NAME", length = 60)
private String firstName;

@Column(name = "MIDDLE_NAME", length = 60)
private String middleName;

@NotEmpty
@Size(max = 60)
@Column(name = "LAST_NAME", length = 60)
@FieldDescription(name = "Last Name", order = 1, type = ExcelColumnType.STRING, required = true)
private String lastName;

@NotNull
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "COMPANY_ID")
@FieldDescription(name = "Company", order = 4, type = ExcelColumnType.COMPANY, required = true)
private Company company;

@ManyToOne
@JoinColumn(name = "USER_GROUP_ID")
private UserGroup userGroup;

//hashcode and equals based on id

}

Here is projection interface

public interface UserDto {

Integer getId();

String getEmailId();

String getFirstName();

String getLastName();

@Value("#{target.firstName + ' ' + target.lastName}")
String getFullName();

Company getCompany();

@Value("#{target.userGroup.id}")
Integer getUserGroupId();

@Value("#{target.userGroup.name}")
String getUserGroupName();

}

This is the repository

@Repository
public interface UserRepository extends JpaRepository<User, Integer>, CacheableRepository<User, Integer> {

//other methods..

@EntityGraph(attributePaths = { "authorities", "company", "userGroup" })
<E> List<E> findBy(Class<E> type);

//another way     
//@Query("Select u from User u left join fetch u.authorities a left join fetch u.company c left join fetch u.userGroup")
//  @QueryHints(value = { @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")})
    //List<User> findBy();
}

Service

@Transactional
public List<UserDto> getAll() {
    return userRepository.findBy(UserDto.class);
}

Note(solution): so de-duplication now only works with Set, earlier it worked with list also. Changed method's return type to set in repository.

0

There are 0 answers