为什么 Spring 数据给我这个反射异常?
Why does Spring Data give me this reflection exception?
我有一些实体、存储库和一个谓词来处理动态查询生成器。当我 运行 一个简单的查询时,我得到一个异常。
- 为什么会出现反射错误?
- 在跟踪的底部附近,它表示无法将 Integer 设置为 String。我不知道为什么那在那里或者是否相关。反正所有的值都是 Integer 所以我什至不知道 String 是从哪里来的。
- 异常发生在对 findAll() 的调用中。我假设 Spring 数据工作正常并且我配置不正确。我只是看不到它是什么。
StackTrace(编辑以减少 post 大小)
2016-11-11 11:52:05,405 [http-bio-8080-exec-13] ERROR com.etisoftware.lib.spring.web.controller.GlobalExceptionHandlerAdvice:34 - An uncaught exception occurred
org.springframework.orm.jpa.JpaSystemException: Error accessing field [private java.lang.Integer com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number] by reflection for persistent property [com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner#number] : 1; nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Integer com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number] by reflection for persistent property [com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner#number] : 1
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Integer com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number] by reflection for persistent property [com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner#number] : 1
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:224)
... 89 more
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Integer field com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number to java.lang.String
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)
at java.lang.reflect.Field.get(Field.java:393)
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:67)
... 133 more
工单实体
@Entity
@Table(name = "workorders")
public class WorkOrder
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "workorder_nbr")
private Integer number;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "subscriber_nbr")
private Subscriber subscriber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "wo_type")
private WorkOrderType type;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pool_nbr")
private WorkerPool pool;
@JoinColumn(name = "wo_status_nbr")
@ManyToOne(fetch = FetchType.LAZY)
private WorkOrderStatus status;
@JoinColumn(name = "wo_owner_nbr")
@ManyToOne(fetch = FetchType.LAZY)
private WorkOrderOwner owner;
@Column(name = "sched_date")
private LocalDate scheduleDate;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "slot_nbr")
private TimeSlot slot;
@Column(name = "auto_complete_date")
private LocalDate autoCompleteDate;
@Column(name = "override")
private Boolean override;
@JoinColumn(name = "wo_location_nbr")
@ManyToOne(fetch = FetchType.LAZY)
private Location location;
/*
* Fetch the notes later in the service using this number because connecting the notes table is a pain
*/
@Column(name = "wo_note_nbr")
private Integer noteNumber;
WorkOrderOwner 实体
@Entity
@Table(name = "wo_owners")
public class WorkOrderOwner
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "wo_owner_nbr")
private Integer number;
@Column(name = "owner_id")
private String name;
@Column(name = "email_address")
private String email_address;
WorkOrderPredicates
@Component
public class WorkOrderPredicates
{
private static DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
private static DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss");
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss");
public Predicate buildQueryFromSearchCriteria(PrintWorkOrderFormDto queryDto)
{
BooleanBuilder builder = new BooleanBuilder();
List<Predicate> predicates = new ArrayList<>();
/*
* TODO I think think this string "entity" which is a variable name is going to give us a problem.
*/
PathBuilder<WorkOrder> entityPath = new PathBuilder<>(WorkOrder.class, "workOrder");
for (SelectedCriteria criteria : queryDto.getCriteria().getRules())
{
assignValueAndOperator(predicates, entityPath, criteria);
}
/*
* Per Chris, the queries will only have one group. That is, there will be a single condition [AND | OR] and then a
* list of criteria.
*/
if (queryDto.getCriteria().getCondition().equals(SelectionFilterCondition.AND))
{
for (Predicate p : predicates)
builder.and(p);
}
else if (queryDto.getCriteria().getCondition().equals(SelectionFilterCondition.OR))
{
for (Predicate p : predicates)
builder.or(p);
}
return builder;
}
@SuppressWarnings("unchecked")
protected static void assignValueAndOperator(List<Predicate> predicates, PathBuilder<?> entityPath, SelectedCriteria criteria)
{
switch (criteria.getOperator())
{
case BETWEEN:
List<LocalDate> values = (List<LocalDate>) criteria.getValue();
LocalDate from = values.get(0);
LocalDate to = values.get(1);
// TODO how do we handle Time separately from Date.
// TODO how do we handle DateTime
predicates.add(entityPath.getDate(criteria.getField(), LocalDate.class).between(from, to));
break;
case BEGINS_WITH:
predicates.add(entityPath.getString(criteria.getField()).like(criteria.getValue() + "%"));
break;
case NOT_BEGINS_WITH:
predicates.add(entityPath.getString(criteria.getField()).notLike(criteria.getValue() + "%"));
break;
case CONTAINS:
predicates.add(entityPath.getString(criteria.getField()).like("%" + criteria.getValue() + "%"));
break;
case NOT_CONTAINS:
predicates.add(entityPath.getString(criteria.getField()).notLike("%" + criteria.getValue() + "%"));
break;
case ENDS_WITH:
predicates.add(entityPath.getString(criteria.getField()).like("%" + criteria.getValue()));
break;
case NOT_ENDS_WITH:
predicates.add(entityPath.getString(criteria.getField()).notLike("%" + criteria.getValue()));
break;
case EQUAL:
predicates.add(entityPath.get(criteria.getField()).eq(extractAndCreateValueOfType(criteria.getValue(), criteria.getType())));
break;
case NOT_EQUAL:
predicates.add(entityPath.get(criteria.getField()).ne(criteria.getValue()));
break;
case GREATER:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).gt((Integer) criteria.getValue()));
break;
case LESS:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).lt((Integer) criteria.getValue()));
break;
case LESS_OR_EQUAL:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).loe((Integer) criteria.getValue()));
break;
case GREATER_OR_EQUAL:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).goe((Integer) criteria.getValue()));
break;
case IN:
/*
* TODO this is causing a hibernate error because its trying to map an integer to a string. This type setting
* below needs to be dynamic based on the selectedcriteria type
*/
List<String> inValues = (List<String>) criteria.getValue();
predicates.add(entityPath.get(criteria.getField(), String.class).in(inValues));
break;
case NOT_IN:
List<String> notInValues = (List<String>) criteria.getValue();
predicates.add(entityPath.get(criteria.getField(), String.class).in(notInValues).not());
break;
case IS_EMPTY:
predicates.add(entityPath.getList(criteria.getField(), List.class).isEmpty());
break;
case IS_NOT_EMPTY:
predicates.add(entityPath.getList(criteria.getField(), List.class).isNotEmpty());
break;
case IS_NOT_NULL:
predicates.add(entityPath.get(criteria.getField()).isNotNull());
break;
case IS_NULL:
predicates.add(entityPath.get(criteria.getField()).isNull());
break;
default:
// TODO implement the default case
break;
}
}
@SuppressWarnings("cast")
private static Object extractAndCreateValueOfType(Object valueObject, SelectionDataType selectionDataType)
{
Object value = null;
switch (selectionDataType)
{
case BOOLEAN:
// TODO
break;
case DATE:
value = LocalDate.parse((String) valueObject, dateFormatter);
break;
case TIME:
value = LocalDateTime.parse((String) valueObject, timeFormatter);
break;
case DATETIME:
value = LocalDateTime.parse((String) valueObject, dateTimeFormatter);
break;
case DOUBLE:
value = (Double) valueObject;
break;
case INTEGER:
value = (Integer) valueObject;
break;
case STRING:
value = (String) valueObject;
break;
default:
break;
}
return value;
}
WorkOrderService 获取方法
@Transactional(transactionManager = PersistenceConfigCbOrgAbstract.txMgrName, readOnly = true)
protected List<WorkOrderDto> fetchWorkOrders(PrintWorkOrderFormDto queryParams, PrintStatus printStatus)
{
List<WorkOrderDto> dtos = new ArrayList<>();
printStatus.setMessage("Fetching work orders");
logger.debug("Query Params: " + queryParams);
for (WorkOrder bean : workOrderRepository.findAll(workOrderPredicates.buildQueryFromSearchCriteria(queryParams)))
{
logger.debug(ToStringBuilder.reflectionToString(bean));
WorkOrderDto dto = modelMapper.map(bean, WorkOrderDto.class);
fetchAndAssignNotes(dto, bean.getNoteNumber());
fetchAndAssignCreateDate(dto, bean.getNumber());
fetchAndAssignPendingDeviceDtos(dto, bean.getNumber());
fetchAndAssignPendingServiceDtos(dto, bean.getNumber());
fetchAndAssignBilling(dto, bean.getNumber());
try
{
dto.setCustomer(new CustomerDto());
fetchAndAssignCustomer(dto);
}
catch (JsonParseException e)
{
logger.error("Could not parse customer information", e);
printStatus.setMessage("Cannot fetch work orders. Please check the application log");
}
catch (JsonMappingException e)
{
logger.error("Could not map customer information", e);
printStatus.setMessage("Cannot fetch work orders. Please check the application log");
}
catch (IOException e)
{
logger.error("Got an IO exception while trying to map customer information", e);
printStatus.setMessage("Cannot fetch work orders. Please check the application log");
}
logger.debug(ToStringBuilder.reflectionToString(dto, ToStringStyle.MULTI_LINE_STYLE, Boolean.TRUE));
printStatus.setRecordCount(printStatus.getRecordCount() + 1);
dtos.add(dto);
}
return dtos;
}
wo_owners table
CREATE TABLE wo_owners (
wo_owner_nbr serial NOT NULL,
owner_id nchar(20) NOT NULL,
email_address nchar(80),
PRIMARY KEY (wo_owner_nbr)
);
工单
CREATE TABLE cborg2001:workorders (
workorder_nbr serial NOT NULL,
subscriber_nbr int,
wo_type int,
pool_nbr int,
wo_status_nbr int,
wo_owner_nbr int,
wo_note_nbr int,
sched_date date,
slot_nbr int,
auto_complete_date date,
override int DEFAULT 0,
wo_location_nbr int,
PRIMARY KEY (workorder_nbr)
);
QueryDSL查询
com.querydsl.jpa.impl.JPAQuery:233 - select workOrder from WorkOrder workOrder where workOrder.scheduleDate = ?1
生成的查询
select
workorder0_.workorder_nbr as workorde1_28_,
workorder0_.auto_complete_date as auto_com2_28_,
workorder0_.wo_location_nbr as wo_locat6_28_,
workorder0_.wo_note_nbr as wo_note_3_28_,
workorder0_.override as override4_28_,
workorder0_.wo_owner_nbr as wo_owner7_28_,
workorder0_.pool_nbr as pool_nbr8_28_,
workorder0_.sched_date as sched_da5_28_,
workorder0_.slot_nbr as slot_nbr9_28_,
workorder0_.wo_status_nbr as wo_stat10_28_,
workorder0_.subscriber_nbr as subscri11_28_,
workorder0_.wo_type as wo_type12_28_
from
workorders workorder0_
where
workorder0_.sched_date=?
我使用以下查询来验证列的数据类型。
SELECT ST.tabname, SC.colname, SC.coltype
FROM systables ST
JOIN syscolumns SC ON SC.tabid = ST.tabid
JOIN sys
WHERE ST.tabname="wo_owners";
它 returns 结果是:
tabname wo_owner_nbr coltype
wo_owners wo_owner_nbr 262
wo_owners owner_id 271
列出了 Informix coltypes here。
262 - 256 = 6 = SERIAL
271 - 256 = 15 = NCHAR
注意* 在 DB-Access 中,偏移值 256 始终添加到这些 coltype 代码,因为 DB-Access 将 SERIAL、SERIAL8 和 BIGSERIAL 列设置为 NOT NULL。 source
"Informix® supports the SERIAL, SERIAL8 and BIGSERIAL data types to produce automatic integer sequences. SERIAL is based on INTEGER (32 bit)" source
据我们所见,确实有字符串来自数据库,但您确保该列被定义为整数数据类型。我刚刚查找了串行语法,它似乎在 postgres 中定义了整数列加上一个序列。
因此我没有看到直接的答案,但想提出一些建议来追踪问题。
use this to see the actual datatype 列在数据库中。
加载 id 涉及的每个 JPA 实体,以验证问题实际上是关于 WorkOrderOwner
删除与问题无关的所有属性
将 number
属性更改为 String 并查看问题是否消失
确认您没有 getter 和 setter 或者它们具有正确的数据类型
让我们知道您的发现
@Transactional
注释需要在 public 方法上。您可以将注释移动到 public 调用代码片段中受保护方法的方法,或者您可以将受保护方法更改为 public.
确切的问题与 Hibernate 或 Spring Data 无关,这是我在 UI 上转换从 Select 元素派生的 ArrayList 的方式的问题。在 WorkOrderPredicates 中我这样做了:
List<String> inValues = (List<String>) criteria.getValue();
这使得值成为一个字符串,并且它不能被赋值给一个整数。
更糟糕的是我后来把它改成了这样:
if (criteria.getType().equals(SelectionDataType.INTEGER))
{
ArrayList<Integer> values = criteria.getValue();
predicates.add(entityPath.get(criteria.getField()).in((values)));
}
else if (criteria.getType().equals((SelectionDataType.STRING)))
{
ArrayList<String> values = criteria.getValue();
predicates.add(entityPath.get(criteria.getField()).in((evalues)));
}
在这里,因为泛型在运行时被擦除,所以 ArrayList 仍然是 ArrayList < String >,即使看起来我正在转换它。
我的解决方案是像这样构建新数组:
if (criteria.getType().equals(SelectionDataType.INTEGER))
{
predicates.add(entityPath.get(criteria.getField()).in((extractArrayListInteger(criteria))));
}
else if (criteria.getType().equals((SelectionDataType.STRING)))
{
predicates.add(entityPath.get(criteria.getField()).in((extractArrayListString(criteria))));
}
创建新数组列表的方法
@SuppressWarnings("unchecked")
private static ArrayList<Integer> extractArrayListInteger(SelectedCriteria criteria)
{
ArrayList<Integer> values = new ArrayList<>();
for (Object v : (ArrayList<Object>) criteria.getValue())
{
values.add(Integer.valueOf(v.toString()));
}
return values;
}
@SuppressWarnings("unchecked")
private static ArrayList<String> extractArrayListString(SelectedCriteria criteria)
{
ArrayList<String> values = new ArrayList<>();
for (String v : (ArrayList<String>) criteria.getValue())
{
values.add(v);
}
return values;
}
我有一些实体、存储库和一个谓词来处理动态查询生成器。当我 运行 一个简单的查询时,我得到一个异常。
- 为什么会出现反射错误?
- 在跟踪的底部附近,它表示无法将 Integer 设置为 String。我不知道为什么那在那里或者是否相关。反正所有的值都是 Integer 所以我什至不知道 String 是从哪里来的。
- 异常发生在对 findAll() 的调用中。我假设 Spring 数据工作正常并且我配置不正确。我只是看不到它是什么。
StackTrace(编辑以减少 post 大小)
2016-11-11 11:52:05,405 [http-bio-8080-exec-13] ERROR com.etisoftware.lib.spring.web.controller.GlobalExceptionHandlerAdvice:34 - An uncaught exception occurred
org.springframework.orm.jpa.JpaSystemException: Error accessing field [private java.lang.Integer com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number] by reflection for persistent property [com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner#number] : 1; nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Integer com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number] by reflection for persistent property [com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner#number] : 1
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Integer com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number] by reflection for persistent property [com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner#number] : 1
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:224)
... 89 more
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Integer field com.etisoftware.workorderprinting.beans.cborg.WorkOrderOwner.number to java.lang.String
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)
at java.lang.reflect.Field.get(Field.java:393)
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:67)
... 133 more
工单实体
@Entity
@Table(name = "workorders")
public class WorkOrder
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "workorder_nbr")
private Integer number;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "subscriber_nbr")
private Subscriber subscriber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "wo_type")
private WorkOrderType type;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pool_nbr")
private WorkerPool pool;
@JoinColumn(name = "wo_status_nbr")
@ManyToOne(fetch = FetchType.LAZY)
private WorkOrderStatus status;
@JoinColumn(name = "wo_owner_nbr")
@ManyToOne(fetch = FetchType.LAZY)
private WorkOrderOwner owner;
@Column(name = "sched_date")
private LocalDate scheduleDate;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "slot_nbr")
private TimeSlot slot;
@Column(name = "auto_complete_date")
private LocalDate autoCompleteDate;
@Column(name = "override")
private Boolean override;
@JoinColumn(name = "wo_location_nbr")
@ManyToOne(fetch = FetchType.LAZY)
private Location location;
/*
* Fetch the notes later in the service using this number because connecting the notes table is a pain
*/
@Column(name = "wo_note_nbr")
private Integer noteNumber;
WorkOrderOwner 实体
@Entity
@Table(name = "wo_owners")
public class WorkOrderOwner
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "wo_owner_nbr")
private Integer number;
@Column(name = "owner_id")
private String name;
@Column(name = "email_address")
private String email_address;
WorkOrderPredicates
@Component
public class WorkOrderPredicates
{
private static DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
private static DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss");
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss");
public Predicate buildQueryFromSearchCriteria(PrintWorkOrderFormDto queryDto)
{
BooleanBuilder builder = new BooleanBuilder();
List<Predicate> predicates = new ArrayList<>();
/*
* TODO I think think this string "entity" which is a variable name is going to give us a problem.
*/
PathBuilder<WorkOrder> entityPath = new PathBuilder<>(WorkOrder.class, "workOrder");
for (SelectedCriteria criteria : queryDto.getCriteria().getRules())
{
assignValueAndOperator(predicates, entityPath, criteria);
}
/*
* Per Chris, the queries will only have one group. That is, there will be a single condition [AND | OR] and then a
* list of criteria.
*/
if (queryDto.getCriteria().getCondition().equals(SelectionFilterCondition.AND))
{
for (Predicate p : predicates)
builder.and(p);
}
else if (queryDto.getCriteria().getCondition().equals(SelectionFilterCondition.OR))
{
for (Predicate p : predicates)
builder.or(p);
}
return builder;
}
@SuppressWarnings("unchecked")
protected static void assignValueAndOperator(List<Predicate> predicates, PathBuilder<?> entityPath, SelectedCriteria criteria)
{
switch (criteria.getOperator())
{
case BETWEEN:
List<LocalDate> values = (List<LocalDate>) criteria.getValue();
LocalDate from = values.get(0);
LocalDate to = values.get(1);
// TODO how do we handle Time separately from Date.
// TODO how do we handle DateTime
predicates.add(entityPath.getDate(criteria.getField(), LocalDate.class).between(from, to));
break;
case BEGINS_WITH:
predicates.add(entityPath.getString(criteria.getField()).like(criteria.getValue() + "%"));
break;
case NOT_BEGINS_WITH:
predicates.add(entityPath.getString(criteria.getField()).notLike(criteria.getValue() + "%"));
break;
case CONTAINS:
predicates.add(entityPath.getString(criteria.getField()).like("%" + criteria.getValue() + "%"));
break;
case NOT_CONTAINS:
predicates.add(entityPath.getString(criteria.getField()).notLike("%" + criteria.getValue() + "%"));
break;
case ENDS_WITH:
predicates.add(entityPath.getString(criteria.getField()).like("%" + criteria.getValue()));
break;
case NOT_ENDS_WITH:
predicates.add(entityPath.getString(criteria.getField()).notLike("%" + criteria.getValue()));
break;
case EQUAL:
predicates.add(entityPath.get(criteria.getField()).eq(extractAndCreateValueOfType(criteria.getValue(), criteria.getType())));
break;
case NOT_EQUAL:
predicates.add(entityPath.get(criteria.getField()).ne(criteria.getValue()));
break;
case GREATER:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).gt((Integer) criteria.getValue()));
break;
case LESS:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).lt((Integer) criteria.getValue()));
break;
case LESS_OR_EQUAL:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).loe((Integer) criteria.getValue()));
break;
case GREATER_OR_EQUAL:
// TODO this casting might not be right
predicates.add(entityPath.getNumber(criteria.getField(), Integer.class).goe((Integer) criteria.getValue()));
break;
case IN:
/*
* TODO this is causing a hibernate error because its trying to map an integer to a string. This type setting
* below needs to be dynamic based on the selectedcriteria type
*/
List<String> inValues = (List<String>) criteria.getValue();
predicates.add(entityPath.get(criteria.getField(), String.class).in(inValues));
break;
case NOT_IN:
List<String> notInValues = (List<String>) criteria.getValue();
predicates.add(entityPath.get(criteria.getField(), String.class).in(notInValues).not());
break;
case IS_EMPTY:
predicates.add(entityPath.getList(criteria.getField(), List.class).isEmpty());
break;
case IS_NOT_EMPTY:
predicates.add(entityPath.getList(criteria.getField(), List.class).isNotEmpty());
break;
case IS_NOT_NULL:
predicates.add(entityPath.get(criteria.getField()).isNotNull());
break;
case IS_NULL:
predicates.add(entityPath.get(criteria.getField()).isNull());
break;
default:
// TODO implement the default case
break;
}
}
@SuppressWarnings("cast")
private static Object extractAndCreateValueOfType(Object valueObject, SelectionDataType selectionDataType)
{
Object value = null;
switch (selectionDataType)
{
case BOOLEAN:
// TODO
break;
case DATE:
value = LocalDate.parse((String) valueObject, dateFormatter);
break;
case TIME:
value = LocalDateTime.parse((String) valueObject, timeFormatter);
break;
case DATETIME:
value = LocalDateTime.parse((String) valueObject, dateTimeFormatter);
break;
case DOUBLE:
value = (Double) valueObject;
break;
case INTEGER:
value = (Integer) valueObject;
break;
case STRING:
value = (String) valueObject;
break;
default:
break;
}
return value;
}
WorkOrderService 获取方法
@Transactional(transactionManager = PersistenceConfigCbOrgAbstract.txMgrName, readOnly = true)
protected List<WorkOrderDto> fetchWorkOrders(PrintWorkOrderFormDto queryParams, PrintStatus printStatus)
{
List<WorkOrderDto> dtos = new ArrayList<>();
printStatus.setMessage("Fetching work orders");
logger.debug("Query Params: " + queryParams);
for (WorkOrder bean : workOrderRepository.findAll(workOrderPredicates.buildQueryFromSearchCriteria(queryParams)))
{
logger.debug(ToStringBuilder.reflectionToString(bean));
WorkOrderDto dto = modelMapper.map(bean, WorkOrderDto.class);
fetchAndAssignNotes(dto, bean.getNoteNumber());
fetchAndAssignCreateDate(dto, bean.getNumber());
fetchAndAssignPendingDeviceDtos(dto, bean.getNumber());
fetchAndAssignPendingServiceDtos(dto, bean.getNumber());
fetchAndAssignBilling(dto, bean.getNumber());
try
{
dto.setCustomer(new CustomerDto());
fetchAndAssignCustomer(dto);
}
catch (JsonParseException e)
{
logger.error("Could not parse customer information", e);
printStatus.setMessage("Cannot fetch work orders. Please check the application log");
}
catch (JsonMappingException e)
{
logger.error("Could not map customer information", e);
printStatus.setMessage("Cannot fetch work orders. Please check the application log");
}
catch (IOException e)
{
logger.error("Got an IO exception while trying to map customer information", e);
printStatus.setMessage("Cannot fetch work orders. Please check the application log");
}
logger.debug(ToStringBuilder.reflectionToString(dto, ToStringStyle.MULTI_LINE_STYLE, Boolean.TRUE));
printStatus.setRecordCount(printStatus.getRecordCount() + 1);
dtos.add(dto);
}
return dtos;
}
wo_owners table
CREATE TABLE wo_owners (
wo_owner_nbr serial NOT NULL,
owner_id nchar(20) NOT NULL,
email_address nchar(80),
PRIMARY KEY (wo_owner_nbr)
);
工单
CREATE TABLE cborg2001:workorders (
workorder_nbr serial NOT NULL,
subscriber_nbr int,
wo_type int,
pool_nbr int,
wo_status_nbr int,
wo_owner_nbr int,
wo_note_nbr int,
sched_date date,
slot_nbr int,
auto_complete_date date,
override int DEFAULT 0,
wo_location_nbr int,
PRIMARY KEY (workorder_nbr)
);
QueryDSL查询
com.querydsl.jpa.impl.JPAQuery:233 - select workOrder from WorkOrder workOrder where workOrder.scheduleDate = ?1
生成的查询
select
workorder0_.workorder_nbr as workorde1_28_,
workorder0_.auto_complete_date as auto_com2_28_,
workorder0_.wo_location_nbr as wo_locat6_28_,
workorder0_.wo_note_nbr as wo_note_3_28_,
workorder0_.override as override4_28_,
workorder0_.wo_owner_nbr as wo_owner7_28_,
workorder0_.pool_nbr as pool_nbr8_28_,
workorder0_.sched_date as sched_da5_28_,
workorder0_.slot_nbr as slot_nbr9_28_,
workorder0_.wo_status_nbr as wo_stat10_28_,
workorder0_.subscriber_nbr as subscri11_28_,
workorder0_.wo_type as wo_type12_28_
from
workorders workorder0_
where
workorder0_.sched_date=?
我使用以下查询来验证列的数据类型。
SELECT ST.tabname, SC.colname, SC.coltype
FROM systables ST
JOIN syscolumns SC ON SC.tabid = ST.tabid
JOIN sys
WHERE ST.tabname="wo_owners";
它 returns 结果是:
tabname wo_owner_nbr coltype
wo_owners wo_owner_nbr 262
wo_owners owner_id 271
列出了 Informix coltypes here。
262 - 256 = 6 = SERIAL
271 - 256 = 15 = NCHAR
注意* 在 DB-Access 中,偏移值 256 始终添加到这些 coltype 代码,因为 DB-Access 将 SERIAL、SERIAL8 和 BIGSERIAL 列设置为 NOT NULL。 source
"Informix® supports the SERIAL, SERIAL8 and BIGSERIAL data types to produce automatic integer sequences. SERIAL is based on INTEGER (32 bit)" source
据我们所见,确实有字符串来自数据库,但您确保该列被定义为整数数据类型。我刚刚查找了串行语法,它似乎在 postgres 中定义了整数列加上一个序列。
因此我没有看到直接的答案,但想提出一些建议来追踪问题。
use this to see the actual datatype 列在数据库中。
加载 id 涉及的每个 JPA 实体,以验证问题实际上是关于 WorkOrderOwner
删除与问题无关的所有属性
将
number
属性更改为 String 并查看问题是否消失确认您没有 getter 和 setter 或者它们具有正确的数据类型
让我们知道您的发现
@Transactional
注释需要在 public 方法上。您可以将注释移动到 public 调用代码片段中受保护方法的方法,或者您可以将受保护方法更改为 public.
确切的问题与 Hibernate 或 Spring Data 无关,这是我在 UI 上转换从 Select 元素派生的 ArrayList 的方式的问题。在 WorkOrderPredicates 中我这样做了:
List<String> inValues = (List<String>) criteria.getValue();
这使得值成为一个字符串,并且它不能被赋值给一个整数。
更糟糕的是我后来把它改成了这样:
if (criteria.getType().equals(SelectionDataType.INTEGER))
{
ArrayList<Integer> values = criteria.getValue();
predicates.add(entityPath.get(criteria.getField()).in((values)));
}
else if (criteria.getType().equals((SelectionDataType.STRING)))
{
ArrayList<String> values = criteria.getValue();
predicates.add(entityPath.get(criteria.getField()).in((evalues)));
}
在这里,因为泛型在运行时被擦除,所以 ArrayList 仍然是 ArrayList < String >,即使看起来我正在转换它。
我的解决方案是像这样构建新数组:
if (criteria.getType().equals(SelectionDataType.INTEGER))
{
predicates.add(entityPath.get(criteria.getField()).in((extractArrayListInteger(criteria))));
}
else if (criteria.getType().equals((SelectionDataType.STRING)))
{
predicates.add(entityPath.get(criteria.getField()).in((extractArrayListString(criteria))));
}
创建新数组列表的方法
@SuppressWarnings("unchecked")
private static ArrayList<Integer> extractArrayListInteger(SelectedCriteria criteria)
{
ArrayList<Integer> values = new ArrayList<>();
for (Object v : (ArrayList<Object>) criteria.getValue())
{
values.add(Integer.valueOf(v.toString()));
}
return values;
}
@SuppressWarnings("unchecked")
private static ArrayList<String> extractArrayListString(SelectedCriteria criteria)
{
ArrayList<String> values = new ArrayList<>();
for (String v : (ArrayList<String>) criteria.getValue())
{
values.add(v);
}
return values;
}