【问题标题】:Using enum to filter status使用枚举过滤状态
【发布时间】:2019-10-23 12:51:06
【问题描述】:

您好,我有 spring 服务,并且在存储库中我正在尝试按枚举过滤

List<Organization> findAllByStatus(StatusType type);

由于某种原因,这个枚举参数没有传递给 sql 查询。

我在 SQL 中看到where organizati0_.status=?,但没有传递任何参数。

知道是什么原因吗?

枚举:

public enum StatusType {
  ACTIVE,
  TO_BE_DELETED,
  @Deprecated IN_ACTIVE
} 

服务:

public List<Organization> getAllOrganizations() {
        return Lists.newArrayList(organizationRepository.findAllByStatus(StatusType.TO_BE_DELETED));
}

【问题讨论】:

  • 你确定他们没有被通过吗?也许参数只是隐藏在问号后面。
  • 是的,当我添加第二个参数时,我只看到第二个:Hibernate: select ... from organization organizati0_ where organizati0_.status=? and organizati0_.name=? 2019-10-23 16:34:31.003 TRACE 19884 --- [0.1-9090-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [test]
  • 你能发布你的模型吗?我对您为该枚举定义列的部分感兴趣
  • 我猜,在实体类的列定义中使用@Enumerated(EnumType.STRING)
  • public class Organization extends AbstractBaseEntity implements BaseEntity

标签: spring-boot enums spring-data-jpa spring-data


【解决方案1】:
import java.io.Serializable;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.example.demo.jobs.CustomQueryParamMethodInterceptor;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
import org.springframework.util.Assert;

public class MyJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
        extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> {

    private EntityManager entityManager;

    private static final CustomQueryParamMethodInterceptor interceptor = new CustomQueryParamMethodInterceptor();
    /**
     * Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
     *
     * @param repositoryInterface must not be {@literal null}.
     */
    public MyJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
        super(repositoryInterface);
    }

    /**
     * The {@link EntityManager} to be used.
     *
     * @param entityManager the entityManager to set
     */
    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#setMappingContext(org.springframework.data.mapping.context.MappingContext)
     */
    @Override
    public void setMappingContext(MappingContext<?, ?> mappingContext) {
        super.setMappingContext(mappingContext);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.springframework.data.repository.support.
     * TransactionalRepositoryFactoryBeanSupport#doCreateRepositoryFactory()
     */
    @Override
    protected RepositoryFactorySupport doCreateRepositoryFactory() {
        RepositoryFactorySupport support = createRepositoryFactory(entityManager);
        support.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
            @Override
            public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
                factory.addAdvice(interceptor);
            }
        });
        return support;
    }

    /**
     * Returns a {@link RepositoryFactorySupport}.
     *
     * @param entityManager
     * @return
     */
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new JpaRepositoryFactory(entityManager);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() {

        Assert.notNull(entityManager, "EntityManager must not be null!");
        super.afterPropertiesSet();
    }
}

然后给你的spring应用类添加注解

@EnableJpaRepositories(repositoryFactoryBeanClass = MyJpaRepositoryFactoryBean.class)

并创建一个方法拦截器将枚举参数转换为特定类型

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;

import com.example.demo.dto.ParamWrapper;
import com.example.demo.dto.StatusType;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.framework.ReflectiveMethodInvocation;

public class CustomQueryParamMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (invocation instanceof ReflectiveMethodInvocation) {
            ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation)invocation;
            Object[] args = invocation.getArguments();
            if (args != null && args.length > 0) {
                Object[] newArgs = new Object[args.length];
                for (int i=0 ;i<args.length; i++) {
                    if (args[i] instanceof ParamWrapper) {
                        ParamWrapper p = (ParamWrapper)args[i];
                        newArgs[i] = p.getValue();
                    } else {
                        newArgs[i] = args[i];
                    }
                }
                rmi.setArguments(newArgs);
            }
        }
        return invocation.proceed();
    }
}

ParamWrapper 是您的枚举应实现的泛型类型

public interface ParamWrapper<T> {
    T getValue();
}

那么你的枚举应该像这样改变

public enum StatusType implements ParamWrapper<Integer> {
    ACTIVE,
    TO_BE_DELETED,
    @Deprecated IN_ACTIVE;

    @Override
    public Integer getValue() {
        //you should determine your value
        return 1;
    }
}

【讨论】:

  • 基于stackoverflow.com/questions/25161241/… 它应该可以使用简单的枚举设置。我真的需要包装我的枚举吗?
  • 我认为我的解决方案更灵活,您可以将任何枚举甚至任何自定义类型作为参数传递给您的 jpa 存储库方法,但是应该在 \@Entity 对象中使用 \@Enumerated 注释
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 1970-01-01
  • 1970-01-01
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多