spring boot jpa 进行通用多条件动态查询和更新 接口

原因: jpa 没有类似于mybatis的那种 拼接sql的方式 想动态更新 需要使用

CriteriaUpdate的方式 去一直拼接,其实大多数场景只要传入一个非空实体类,去动态拼接sql 

1.定义实体类 继承一个统一的类型

@Data
@ToString
@Entity
@Table(name = "sys_user")
@DynamicInsert
@JsonIgnoreProperties(ignoreUnknown = true)
@EntityListeners(SysUserListener.class)
public class SysUser extends BaseEntity {
    // ...

    @Schema(description = "用户ID")
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long userId;

    // ...

    @Schema(description = "用户账号")
    @NotBlank(message = "用户账号不能为空")
    @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
    @Column(name = "user_name")
    private String userName;

}

2. java 反射 拿到id 和值 作为 更新的 主键

package com.ricoh.srcb.system.service;

import com.ricoh.srcb.system.entity.domain.SysMenu;
import com.ricoh.srcb.system.framework.web.domain.BaseEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.apache.commons.lang3.ObjectUtils;

import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public abstract class GenericService<T extends BaseEntity> {

    @PersistenceContext
    private EntityManager entityManager;

    /**
     * Updates the given entity by setting non-null fields.
     *
     * @param entity The entity to update.
     * @return The number of rows affected by the update.
     */
    public int baseUpdate(T entity) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaUpdate<T> criteriaUpdate = criteriaBuilder.createCriteriaUpdate(entityClass());
        Root<T> root = criteriaUpdate.from(entityClass());


        Field idField = getIdFieldAndValue(entity).getKey();
        Object idValue = getIdFieldAndValue(entity).getValue();


        if (ObjectUtils.isEmpty(idValue)) {
            throw new RuntimeException("jpa update id not null");
        }
        criteriaUpdate.where(criteriaBuilder.equal(root.get(idField.getName()), idValue));
        boolean hasFieldsToUpdate = false;

        Field[] fields = entity.getClass().getDeclaredFields();

        for (Field field : fields) {
            boolean annotationPresent = field.isAnnotationPresent(Column.class);
            if (!idField.getName().equals(field.getName())&&annotationPresent) { // Exclude the id field
                try {
                    field.setAccessible(true);
                    Object value = field.get(entity);
                    if (ObjectUtils.isNotEmpty(value)) {
                        criteriaUpdate.set(field.getName(), value);
                        hasFieldsToUpdate = true;
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Error accessing field: " + field.getName(), e);
                }
            }
        }

        if (!hasFieldsToUpdate) {
            throw new IllegalArgumentException("No fields to update");
        }
        log.info("update table {} ,sql:{}", entity.getClass().getName(), criteriaUpdate);
        return entityManager.createQuery(criteriaUpdate).executeUpdate();
    }

    private Map.Entry<Field, Object> getIdFieldAndValue(T entity) {
        for (Field field : entity.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                field.setAccessible(true);
                try {
                    Object value = field.get(entity);
                    return new AbstractMap.SimpleEntry<>(field, value);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Error accessing @Id field.", e);
                }
            }
        }
        throw new IllegalStateException("No @Id field found in entity class.");
    }

    /**
     * Finds an entity by its ID.
     *
     * @param id The ID of the entity to find.
     * @return The found entity or null if not found.
     */
    /**
     * Finds entities based on non-null fields of the provided entity.
     *
     * @param queryEntity The entity used as a template for query conditions.
     * @return A list of entities that match the query conditions.
     */
    public List<T> findByCriteria(T queryEntity) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(entityClass());
        Root<T> root = criteriaQuery.from(entityClass());

        List<Predicate> predicates = new ArrayList<>();


        Field[] fields = entityClass().getDeclaredFields();


        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object value = field.get(queryEntity);
                if (ObjectUtils.isNotEmpty(value)) {
                    predicates.add(criteriaBuilder.equal(root.get(field.getName()), value));
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Error accessing field: " + field.getName(), e);
            }
        }

        criteriaQuery.where(predicates.toArray(new Predicate[0]));
        return entityManager.createQuery(criteriaQuery).getResultList();
    }

    private Class<T> entityClass() {
        Type superClass = getClass().getGenericSuperclass();
        if (superClass instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) superClass;
            Type entityType = parameterizedType.getActualTypeArguments()[0];
            if (entityType instanceof Class) {
                return (Class<T>) entityType;
            } else {
                throw new IllegalArgumentException("Expected a class type for the generic parameter but got " + entityType);
            }
        } else {
            throw new IllegalArgumentException("Expected a ParameterizedType but got " + superClass);
        }
    }

    public abstract List<SysMenu> selectMenuList(SysMenu menu, Long userId);
}

 3.service 进行 继承这个类 进行通用查询和更新

@Service
public class SysMenuServiceImpl extends GenericService<SysMenu> implements ISysMenuService {
@Override
@Transactional
public Boolean updateRole(SysRole role) {
// 修改角色信息
baseUpdate(role);
return insertRoleMenu(role) > 0 ? true : false;
}

}

 

posted @ 2024-06-18 17:38  夜半钟声到客船  阅读(102)  评论(0编辑  收藏  举报