Hibernate AttributeConverter завершается ошибкой, если используется пользовательский запрос с JOIN
Я нашел очень интересную ошибку в спящем 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 id1_11_, taskentity0_.amount as amount2_11_, taskentity0_.comment as comment3_11_, taskentity0_.commission as commissi4_11_, taskentity0_.created as created5_11_, taskentity0_.currency as currency6_11_, taskentity0_.current_account_id as current14_11_, taskentity0_.current_object_id as current15_11_, taskentity0_.current_user_id as current16_11_, taskentity0_.description as descript7_11_, taskentity0_.data as data8_11_, taskentity0_.initiator_account_id as initiat17_11_, taskentity0_.initiator_object_id as initiat18_11_, taskentity0_.initiator_user_id as initiat19_11_, taskentity0_.payment_method as payment_9_11_, taskentity0_.status as status10_11_, taskentity0_.title as title11_11_, taskentity0_.updated as updated12_11_, taskentity0_.version as version13_11_ 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
, Код из проекта упрощен и содержит только информацию, необходимую для этого примера (Вы можете увидеть некоторую избыточную информацию в журналах). Спасибо за помощь.
0 ответов
Попробуй это:
select t from TaskEntity t where t.status in (:statuses) and t.involvedUsers.id in (:involvedUserIds) order by t.id DESC