jpa中枚举类型保存和查询

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);
}

 





posted @ 2023-07-24 15:47  穿越到打工界  阅读(681)  评论(0)    收藏  举报