1,存储枚举字段时,保存在数据库中的值
默认情况使用枚举类型的ordinal值,该值是枚举值定义顺序,从0,1,2。。。依次类推
@Enumerated(EnumType.ORDINAL) private SignUpStatus status;
使用枚举值名称
@Enumerated(EnumType.STRING) private SignUpStatus status;
2,使用native sql查询枚举类型字段时,返回值封装问题
jpa框架对于返回结果封装,简单使用是美好的,但实际情况都是复杂难以预料的,对于不了解细节的人来说,总会遇到莫名奇妙的问题。
在jpa中用hql查询时,不用再调用以下方法
dataQuery.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(getClazz()));
因为hql对象查询是严格关联的,其查询后结果自动被封装为对应的bean,再次调用封装类型是多余的。并且hql查询的列不在实体中时,在初次查询结果中是存在的,但由于hql执行框架第一次只封装的与实体对应的列(严格对应),而实体不存在的对应的列结果丢失,导致即便调用上面的代码也取不到结果。相当于将默认转换的bean(不含不在实体中的列)再次转换。
hql查询的好处就是,不用写sql,不用在意字段列名的对应,可以减少写sql带来的错误;对象自动封装,封装时能处理常见类型,缺点就是复杂查询难以使用。
在jpa中使用native sql查询时,可以解决hql难以完成的一些查询问题,可以使用上面的代码对查询的直接结果直接封装(包括得到不在实体上的列结果)。缺点是封装为bean时不能友好的处理枚举类型,包装类型等类型的字段。也没有提供比较容易、有效的可以自行处理类型的接口。
解决这个问题修改源码+在类中实现多态,将修改后的代码按源码所在包名,放到项目src/main/java下即可(查看jdk ClassLoader如何加载相同报名类名的?)。
例如:
spring boot jpa 2.52版本 PropertyAccessBasicImpl
以下是PropertyAccessBasicImpl源码作用是获得bean字段get、set方法对象,修改为将枚举类,Long型包装类默认setter方法改为指定的多态方法,然后再相应类中实现对方法,完成结果转换
/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.property.access.internal; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.property.access.spi.*; import org.jboss.logging.Logger; import java.lang.reflect.Method; import java.math.BigInteger; /** * PropertyAccessor for accessing the wrapped property via get/set pair, which may be nonpublic. * * @author Steve Ebersole * * @see PropertyAccessStrategyBasicImpl */ public class PropertyAccessBasicImpl implements PropertyAccess { private static final Logger log = Logger.getLogger( PropertyAccessBasicImpl.class ); private final PropertyAccessStrategyBasicImpl strategy; private final GetterMethodImpl getter; private final SetterMethodImpl setter; public PropertyAccessBasicImpl( PropertyAccessStrategyBasicImpl strategy, Class containerJavaType, final String propertyName) { this.strategy = strategy; final Method getterMethod = ReflectHelper.findGetterMethod( containerJavaType, propertyName ); this.getter = new GetterMethodImpl( containerJavaType, propertyName, getterMethod ); if(getterMethod.getReturnType().isEnum()){ final Method setterMethod = ReflectHelper.findSetterMethod( containerJavaType, propertyName, String.class ); this.setter = new SetterMethodImpl( containerJavaType, propertyName, setterMethod ); }else if(getterMethod.getReturnType().isAssignableFrom(Long.class)){ final Method setterMethod = ReflectHelper.findSetterMethod( containerJavaType, propertyName, BigInteger.class ); this.setter = new SetterMethodImpl( containerJavaType, propertyName, setterMethod ); }else{ final Method setterMethod = ReflectHelper.findSetterMethod( containerJavaType, propertyName, getterMethod.getReturnType() ); this.setter = new SetterMethodImpl( containerJavaType, propertyName, setterMethod ); } } @Override public PropertyAccessStrategy getPropertyAccessStrategy() { return strategy; } @Override public Getter getGetter() { return getter; } @Override public Setter getSetter() { return setter; } }
类中status字段的两个set方法
public void setStatus(SignUpStatus status) { this.status = status; } public void setStatus(String status) { this.status = SignUpStatus.getByOriginal(status); }