我有一些抛出可怕的 ClassCastException 的 JPA 代码(以 Hibernate 作为提供者)。然而,它似乎只发生在某些计算机上,而不是其他具有完全相同的代码库、配置、数据库架构和数据的计算机上。有没有其他人遇到过这个?有没有办法解决它?我相当肯定这不是我的代码中的错误,因为它在某些环境中工作(在较新的 Macbook Pros 的开发机器上)但在其他环境(Mac Mini 和 Amazon Linux AMI)中没有。
作为参考,这在尝试使用摘要身份验证通过 Spring Security 对用户进行身份验证时显示。Spring 配置为通过调用实现 UserDetailsService 接口的 bean 来检索用户列表:
@Component("httpDigestUserDetailsService")
public class HttpDigestUserDetailsService implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(HttpDigestUserDetailsService.class);
@Autowired
UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<User> users = userService.findBy("username", username);
User user = users.get(0);
return new HttpDigestUserDetails(user);
}
调用时出现错误userService.findBy()
。 UserService
实现方式UserServiceImpl
如下:
@Transactional(propagation = Propagation.REQUIRED)
public class UserServiceImpl extends AbstractService<User> implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public Class getPersistentObjectClass() {
return User.class;
}
public void signUp(User user) {
user.addUserRole(UserRole.ROLE_USER);
save(user);
}
}
真正的findBy
方法是在AbstractRestService
:
@Transactional(propagation = Propagation.REQUIRED)
public abstract class AbstractService<T extends PO> implements EntityService<T> {
public List<T> findBy(String ... fields) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i=0; i<fields.length - 1; i+=2) {
map.put(fields[i], fields[i+1]);
}
return findBy(map);
}
public List<T> findBy(Map<String, Object> fields) {
String queryString = String.format("SELECT o FROM %s o", getPersistentObjectClass().getName());
StringBuilder sb = new StringBuilder();
Set<String> fieldsSet = fields.keySet();
boolean first = true;
for (String fieldName : fieldsSet) {
if (first) { sb.append(" WHERE "); first = false; } else sb.append(" AND ");
sb.append(String.format("o.%s = :%s", fieldName, fieldName));
}
queryString = queryString + sb.toString();
TypedQuery<T> q = getEntityManager().createQuery(queryString, getPersistentObjectClass());
for (String fieldName : fieldsSet) {
q.setParameter(fieldName, fields.get(fieldName));
}
List<T> results = q.getResultList();
return results;
}
}
User
域对象注释如下(省略只是简单属性的 getter/setter :
@Entity
@Table(name = "users")
public class User extends PO {
private Patient patient;
private Provider provider;
private Set<UserRole> userRoles;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "patient_id", nullable = true)
public Patient getPatient() {
return Patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "provider_id", nullable = true)
public Provider getProvider() {
return provider;
}
public void setProvider() {
this.provider = provider;
}
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
}
最后,PO
所有域对象扩展的基础对象:
@MappedSuperclass
public class PO implements Serializable {
private Long id;
@Id
@GeneratedValue
@Column(name = "id")
public Long getId() {
return id;
}
}
Patient
并且Provider
还扩展 PO,并将反向映射返回给用户。如果看到它有帮助,我可以添加该代码。
最后,这是我收到的异常和堆栈跟踪:
HTTP Status 500 - org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of PO.id; nested exception is javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of PO.id
java.lang.IllegalArgumentException: java.lang.ClassCastException@7e46a468
sun.reflect.GeneratedMethodAccessor57.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:164)
org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:341)
org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4491)
org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:4213)
org.hibernate.engine.internal.ForeignKeys.isTransient(ForeignKeys.java:209)
org.hibernate.engine.spi.CascadingAction$8.noCascade(CascadingAction.java:375)
org.hibernate.engine.internal.Cascade.cascade(Cascade.java:176)
org.hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:160)
org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:151)
org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88)
org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:75)
org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:512)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
com.luminatehealth.service.UserServiceImpl$$EnhancerByCGLIB$$2a6af235.findBy(<generated>)
com.luminatehealth.rest.security.HttpDigestUserDetailsService.loadUserByUsername(HttpDigestUserDetailsService.java:33)
org.springframework.security.web.authentication.www.DigestAuthenticationFilter.doFilter(DigestAuthenticationFilter.java:144)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
我已经阅读了有关在加载从共享超类扩展的域对象时如何创建代理对象的 Hibernate 问题,但是我看到的大多数建议都说您应该确保将注释放在属性而不是字段上(我们已经在做),或者使用预先加载来避免创建代理(我们所有的关联都已经使用预先加载)。我也对为什么要创建代理感到困惑,因为我认为这只发生在您需要延迟加载关联时。在这种情况下,我假设它与事务管理有关。
两天来,我一直在为此苦苦挣扎,但进展甚微,因此我将不胜感激。