Сбой Hibernate AttributeConverter при использовании пользовательского запроса с JOIN

Я нашел очень интересную ошибку в hibernate jpa 2.1. Когда я пишу реализацию пользовательского запроса в репозитории и использую в запросе как пользовательский AttributeConverter, так и JOIN, AttributeConverter не работает. Я уверен, что JOIN вызывает эту проблему, но я не знаю, как ее решить. Без JOIN AttributeConverter запрос работает нормально. Я нашел одно уродливое решение (вы можете увидеть его в моем коде), но я ищу правильное.

Итак, в случае с JOIN для запроса

SELECT t FROM TaskEntity t JOIN t.involvedUsers u WHERE status in :statuses AND u.id IN :involvedUserIds ORDER BY t.id DESC 

Параметры запроса:

statuses = [CREATED, APPROVED, IN_PROGRESS];
involvedUserIds = [1, 2];

Я получил следующую ошибку:

2018-02-26 15:39:36.458 DEBUG 2482 --- [nio-8008-exec-1] org.hibernate.SQL                        : select taskentity0_.id as id1spring-data-jpa 1.5.8.RELEASE, taskentity0_.amount as amount2spring-data-jpa 1.5.8.RELEASE, taskentity0_.comment as comment3spring-data-jpa 1.5.8.RELEASE, taskentity0_.commission as commissi4spring-data-jpa 1.5.8.RELEASE, taskentity0_.created as created5spring-data-jpa 1.5.8.RELEASE, taskentity0_.currency as currency6spring-data-jpa 1.5.8.RELEASE, taskentity0_.current_account_id as current14spring-data-jpa 1.5.8.RELEASE, taskentity0_.current_object_id as current15spring-data-jpa 1.5.8.RELEASE, taskentity0_.current_user_id as current16spring-data-jpa 1.5.8.RELEASE, taskentity0_.description as descript7spring-data-jpa 1.5.8.RELEASE, taskentity0_.data as data8spring-data-jpa 1.5.8.RELEASE, taskentity0_.initiator_account_id as initiat17spring-data-jpa 1.5.8.RELEASE, taskentity0_.initiator_object_id as initiat18spring-data-jpa 1.5.8.RELEASE, taskentity0_.initiator_user_id as initiat19spring-data-jpa 1.5.8.RELEASE, taskentity0_.payment_method as payment
@NoRepositoryBean
public interface CustomTaskRepository {
    List<TaskEntity> find(List<TaskStatuses> statuses, List<Integer> involvedUserIds)
}
11_, taskentity0_.status as status10spring-data-jpa 1.5.8.RELEASE, taskentity0_.title as title11spring-data-jpa 1.5.8.RELEASE, taskentity0_.updated as updated12spring-data-jpa 1.5.8.RELEASE, taskentity0_.version as version13spring-data-jpa 1.5.8.RELEASE from public.tasks taskentity0_ inner join public.tasks_users involvedus1_ on taskentity0_.id=involvedus1_.task_id inner join public.users userentity2_ on involvedus1_.user_id=userentity2_.id where (status in (? , ? , ?)) and (userentity2_.id in (? , ?)) order by taskentity0_.id DESC limit ? 2018-02-26 15:39:36.459 TRACE 2482 --- [nio-8008-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARBINARY] - [CREATED] 2018-02-26 15:39:36.460 TRACE 2482 --- [nio-8008-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARBINARY] - [APPROVED] 2018-02-26 15:39:36.460 TRACE 2482 --- [nio-8008-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARBINARY] - [IN_PROGRESS] 2018-02-26 15:39:36.460 TRACE 2482 --- [nio-8008-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [INTEGER] - [1] 2018-02-26 15:39:36.461 TRACE 2482 --- [nio-8008-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [INTEGER] - [2] org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = bytea No operator matches the given name and argument type(s). You might need to add explicit type casts.

Код ниже:

@Repository
public class TaskRepositoryImpl implements CustomTaskRepository {

    private final EntityManager em;


    @Autowired
    public TaskRepositoryImpl(JpaContext context) {
        this.em = context.getEntityManagerByManagedType(TaskEntity.class);
    }
    public List<TaskEntity> find(List<TaskStatuses> statuses, List<Integer> involvedUserIds) {
        Map<String, Object> params = new HashMap<>();
        StringBuilder queryStr = new StringBuilder("SELECT t FROM TaskEntity t WHERE");

        if (statuses != null && !statuses.isEmpty()) {
            queryStr.append(" status in :statuses AND");
            params.put("statuses", involvedUserIds == null ? statuses :
                statuses.stream().map(TaskStatuses::getId).collect(Collectors.toList())); //problem is here
        }
        if (involvedUserIds != null && !involvedUserIds.isEmpty()) {
            queryStr.insert(queryStr.indexOf("WHERE"), "JOIN t.involvedUsers u "); //this join causes the problem
            queryStr.append(" u.id IN :involvedUserIds AND");
            params.put("involvedUserIds", involvedUserIds);
        }
        if (queryStr.lastIndexOf(" WHERE") == queryStr.length() - 6)
            queryStr.setLength(queryStr.length() - 6);
        else
            queryStr.setLength(queryStr.length() - 4);
        Query query = em.createQuery(queryStr.toString());
        params.forEach(query::setParameter);
        query.setFirstResult(0);
        query.setMaxResults(20);
        return query.getResultList();
    }
}

Сущность задачи:

@Getter @Setter
@Entity
@Table(name = "tasks")
public class TaskEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String title;

    private String description;

    @NotNull
    @Convert(converter = TaskStatusesConverter.class)
    private TaskStatuses status;

    @ManyToMany
    @JoinTable(
        name = "tasks_users",
        joinColumns = @JoinColumn(name = "task_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
        foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT),
        inverseForeignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
    private Set<UserEntity> involvedUsers;
}

Объект пользователя

@Getter @Setter
@Entity
@Table(name = "users")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @NotNull
    @Column(unique = true)
    private String email;
}

Статусы задач:

public enum TaskStatuses {
    CREATED(1),
    APPROVED(2),
    IN_PROGRESS(3),
    COMPLETED(4),

    REJECTED(100),
    ;

    private Integer id;

    TaskStatuses(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public static TaskStatuses valueOf(Integer id) {
        for (TaskStatuses value : values())
            if (value.getId().equals(id))
                return value;
        return null;
    }
}

Конвертер статусов задач:

public class TaskStatusesConverter implements AttributeConverter<TaskStatuses, Integer> {

    @Override
    public Integer convertToDatabaseColumn(TaskStatuses status) {
        return status.getId();
    }

    @Override
    public TaskStatuses convertToEntityAttribute(Integer status) {
        return TaskStatuses.valueOf(status);
    }
}

И репозиторий:

@NoRepositoryBean
public interface CustomTaskRepository {
    List<TaskEntity> find(List<TaskStatuses> statuses, List<Integer> involvedUserIds)
}

В этом проекте я использую spring-boot 1.5.8.RELEASE и spring-data-jpa 1.5.8.RELEASE. Код из проекта упрощен и содержит только информацию, необходимую для этого примера (вы можете увидеть избыточную информацию в логах). Спасибо за помощь.


person Bohdan Petrenko    schedule 26.02.2018    source источник
comment
Я получил следующую ошибку Я вижу только сообщения DEBUG и TRACE, ошибок нет   -  person Jens    schedule 26.02.2018
comment
Спасибо. Вопрос обновлен   -  person Bohdan Petrenko    schedule 26.02.2018
comment
Возможно, status должно быть t.status? потому что это не квалифицировано   -  person    schedule 26.02.2018
comment
Я использую квалификатор :statuses   -  person Bohdan Petrenko    schedule 26.02.2018
comment
Уверены, что использование списка целых чисел правильно для :statuses? Из-за AttributeConverters это должен быть список перечислений imho   -  person tom    schedule 26.02.2018
comment
params.put(статусы, вовлеченныеUserIds == null ? Статусы: statuses.stream().map(TaskStatuses::getId).collect(Collectors.toList())); очень подозрительно, потому что, когда задействованный UserIds равен нулю, вы используете List‹TaskStatuses›, но когда это не так, вы используете List‹Integer›. Это не может быть правильным, это должен быть List‹TaskStatuses› в обоих случаях.   -  person Kirinya    schedule 27.02.2018


Ответы (1)


Попробуй это:

select t from TaskEntity t where t.status in (:statuses) and t.involvedUsers.id in (:involvedUserIds) order by t.id DESC
person Sana    schedule 10.07.2019