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

浙公网安备 33010602011771号