Spring JPA 之非全量更新实现

不废话直接上代码

定义接口

public interface BaseService {

    T update(T t);
}

然后实现

import com.feijia.pregnant.exceptions.FJException;
import org.apache.commons.beanutils.BeanMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.*;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * JPA非全量更新实现,由于动态代理的原因,必须以接口方式实现
 * 建议在实际的使用过程中,接口需要继承BaseService,实现类需要继承BaseServiceImpl
 * 如果已经继承的是不太容易方便更改的库文件
 * 可以使用这种方式
 * @Autowired
 * BaseService baseService;
 * 然后调用.update(T)即可
 * @author Connor
 * 2020-03-09
 * @param  加了@Entity注解的数据库模型
 */
@Service
public class BaseServiceImpl implements BaseService {

    @Autowired
    EntityManager entityManager;

    /**
     * JPA默认更新必须加入事务管理,所以这里的@Transactional注解不能省略掉
     * @param t
     * @return
     */
    @Override
    @Transactional
    public T update(T t) {
        // 先把对象转map,这里会自动过滤null值
        Map map = new BeanMap(t);
        Iterator iterator = map.keySet().iterator();
        // 获取实体类的@Entity注解
        Entity entity = t.getClass().getAnnotation(Entity.class);
        if (entity == null) {
            // 因为BeanMap会放置一个key为class的键值对,所以这里需要异常处理一下
            throw new FJException(new NullPointerException());
        }
        StringBuilder stringBuilder = new StringBuilder();
        // 根据注解拿到方法名,然后生成最基本的更新语句
        stringBuilder.append("UPDATE ");
        stringBuilder.append(entity.name());
        stringBuilder.append(" ");
        Field idField = null;
        // 这里决定是SET还是字段
        boolean flag = false;
        while (iterator.hasNext()) {
            String key = iterator.next();
            Field field;
            try {
                field = t.getClass().getDeclaredField(key);
            } catch (NoSuchFieldException e) {
                continue;
            }
            if (field.getAnnotation(Id.class) == null && field.getAnnotation(Transient.class) == null) {
                if (map.get(key) != null) {
                    if (!flag) {
                        // 如果是第一次,加一个SET
                        stringBuilder.append("SET ");
                        flag = true;
                    } else {
                        // 如果不是第一次,加一个逗号
                        stringBuilder.append(" , ");
                    }
                    // 这里拼接sql语句
                    stringBuilder.append(toLine(key));
                    stringBuilder.append(" = '");
                    stringBuilder.append(map.get(key).toString());
                    stringBuilder.append("' ");
                }
            } else if (field.getAnnotation(Id.class) != null) {
                // 保存一下id的字段
                idField = field;
            }
        }
        // 这里直接把where放在外面,默认更新必须提供条件,避免预期之外的严重错误
        stringBuilder.append(" WHERE ");
        if (idField != null) {
            // 拼接一下条件,这里是id,如果是别的条件,可以使用map传参或其他实现方式
            stringBuilder.append(idField.getName() + " = '" + map.get(idField.getName()) + "'");
        }
        Query dataQuery = entityManager.createNativeQuery(stringBuilder.toString());
        dataQuery.executeUpdate();
        return t;
    }

    /**
     * 驼峰 转下划线
     * @param camelCase
     * @return
     */
    public static String toLine(String camelCase){
        Pattern humpPattern = Pattern.compile("[A-Z]");
        Matcher matcher = humpPattern.matcher(camelCase);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()){
            matcher.appendReplacement(sb, "_"+matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}

使用也是非常简单

@Autowired
BaseService baseService;

然后就baseService.update就OK了。
也可以添加到通用接口中,那么继承的service就有这个方法了

你可能感兴趣的:(Spring JPA 之非全量更新实现)