Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select distinct results in Spring Data

I have a problem to build query in Spring Data using simple Spring Data query or @Query or QueryDSL.

How to select rows that will be distinct for three columns (Study, Country, Login) and as a result of query will be the list of objects type of User?

Table:

------------------------------------- | User | ------------------------------------- | Id | Study | Country | Site | Login | ------------------------------------- | 1 | S1 | US | 11 | user1 | | 2 | S1 | US | 22 | user1 | | 3 | S1 | US | 33 | user1 | | .. | .. | .. | .. | .. | -------------------------------------

I need a query which based only on Study will return unique Users for each Login and Country only and without taking into account the Site column.

Method signature is like below:

List<User> findByStudyIgnoreCase(String study);

and right now is returning all rows from table Users. So I have duplicated rows about user assignments in Study and Country because I have UI presentation in other table where Site is not needed.

So, I need something like:

select distinct Study, Country, Login from User

but returning object must be the User object just like the method signature says (for example first of matching result).

How can it be done?

  1. Is it possible in this Way or similar way? How to make it correct?

    @Query("SELECT DISTINCT s.study, s.country, s.login FROM user s where s.study = ?1 ") List<User> findByStudyIgnoreCase(String study);

  2. Is it possible using QueryDSL?

---- EDIT ----

I tried to write query via QueryDSL like TimoWestkämper suggested but I have a problem.

    public List<User> findByStudyIgnoreCase(String study) {
        QUser $ = QUser.user;
        BooleanExpression studyExists = $.study.equalsIgnoreCase(study);

        List<Users> usersList = from($)
            .where(studyExists)
            .distinct()
            .list(Projections.bean(User.class, $.study, $.country, $.id.login));

        return usersList;
    }

After call above query the exception occurs:

org.springframework.dao.InvalidDataAccessApiUsageException: The bean of type: com.domain.app.model.User has no property called: study; nested exception is java.lang.IllegalArgumentException: The bean of type: com.domain.app.model.User has no property called: study

Why it happens?

---- EDIT 2 ----

My User class:

@Entity
@Table(name="USER")
@Immutable
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter @EqualsAndHashCode @ToString
@FieldDefaults(level=AccessLevel.PRIVATE)
public class User {

    @EmbeddedId
    UserId id;

    @Column(name="CONTACT_UNIQUE_ID")
    String contactUniqueId;

    String country;

    @Column(name="COUNTRY_CODE")
    String countryCode;

    @Column(name="STUDY")
    String study;

    String firstname;

    String lastname;

    String email;

    String role;
}

Embeddable UserId class:

@Embeddable
@Getter @EqualsAndHashCode @ToString
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level=AccessLevel.PRIVATE)
public class UserId implements Serializable {

    private static final long serialVersionUID = 1L;

    String site;
    String login;

}

Generated QUser class:

@Generated("com.mysema.query.codegen.EntitySerializer")
public class QUser extends EntityPathBase<User> {

    private static final long serialVersionUID = 1646288729;

    private static final PathInits INITS = PathInits.DIRECT;

    public static final QUser user = new User("user");

    public final StringPath contactUniqueId = createString("contactUniqueId");

    public final StringPath country = createString("country");

    public final StringPath countryCode = createString("countryCode");

    public final StringPath study = createString("study");

    public final StringPath email = createString("email");

    public final StringPath firstname = createString("firstname");

    public final QUser id;

    public final StringPath lastname = createString("lastname");

    public final StringPath role = createString("role");

    public QUser(String variable) {
        this(User.class, forVariable(variable), INITS);
    }

    @SuppressWarnings("all")
    public QUser(Path<? extends User> path) {
        this((Class)path.getType(), path.getMetadata(), path.getMetadata().isRoot() ? INITS : PathInits.DEFAULT);
    }

    public QUser(PathMetadata<?> metadata) {
        this(metadata, metadata.isRoot() ? INITS : PathInits.DEFAULT);
    }

    public QUser(PathMetadata<?> metadata, PathInits inits) {
        this(User.class, metadata, inits);
    }

    public QUser(Class<? extends User> type, PathMetadata<?> metadata, PathInits inits) {
        super(type, metadata, inits);
        this.id = inits.isInitialized("id") ? new QUser(forProperty("id")) : null;
    }

}
like image 336
Roman Avatar asked Oct 12 '25 14:10

Roman


1 Answers

I can answer the Querydsl part. It works via

List<User> users = query.from(user)
  .where(user.study.eq(arg))
  .distinct()
  .list(Projections.fields(User.class, user.study, user.country, user.login));

You will get User instances with populated study, country and login fields out. The User instances are not managed JPA entities, but populated beans.

Alternatively you can query for Tuple instances like this

List<Tuple> tuples = query.from(user)
  .where(user.study.eq(arg))
  .distinct()
  .list(user.study, user.country, user.login);

But as you are using Spring Data you might want to return Users instead.

like image 154
Timo Westkämper Avatar answered Oct 14 '25 03:10

Timo Westkämper



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!