Spring JPA 之非全量更新實(shí)現(xiàn)

不廢話直接上代碼

定義接口

public interface BaseService<T> {

    T update(T t);
}

然后實(shí)現(xiàn)

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非全量更新實(shí)現(xiàn),由于動(dòng)態(tài)代理的原因,必須以接口方式實(shí)現(xiàn)
 * 建議在實(shí)際的使用過(guò)程中,接口需要繼承BaseService,實(shí)現(xiàn)類需要繼承BaseServiceImpl
 * 如果已經(jīng)繼承的是不太容易方便更改的庫(kù)文件
 * 可以使用這種方式
 * @Autowired
 * BaseService<T> baseService;
 * 然后調(diào)用.update(T)即可
 * @author Connor
 * 2020-03-09
 * @param <T> 加了@Entity注解的數(shù)據(jù)庫(kù)模型
 */
@Service
public class BaseServiceImpl<T> implements BaseService<T> {

    @Autowired
    EntityManager entityManager;

    /**
     * JPA默認(rèn)更新必須加入事務(wù)管理,所以這里的@Transactional注解不能省略掉
     * @param t
     * @return
     */
    @Override
    @Transactional
    public T update(T t) {
        // 先把對(duì)象轉(zhuǎn)map,這里會(huì)自動(dòng)過(guò)濾null值
        Map<String, Object> map = new BeanMap(t);
        Iterator<String> iterator = map.keySet().iterator();
        // 獲取實(shí)體類的@Entity注解
        Entity entity = t.getClass().getAnnotation(Entity.class);
        if (entity == null) {
            // 因?yàn)锽eanMap會(huì)放置一個(gè)key為class的鍵值對(duì),所以這里需要異常處理一下
            throw new FJException(new NullPointerException());
        }
        StringBuilder stringBuilder = new StringBuilder();
        // 根據(jù)注解拿到方法名,然后生成最基本的更新語(yǔ)句
        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) {
                        // 如果是第一次,加一個(gè)SET
                        stringBuilder.append("SET ");
                        flag = true;
                    } else {
                        // 如果不是第一次,加一個(gè)逗號(hào)
                        stringBuilder.append(" , ");
                    }
                    // 這里拼接sql語(yǔ)句
                    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放在外面,默認(rèn)更新必須提供條件,避免預(yù)期之外的嚴(yán)重錯(cuò)誤
        stringBuilder.append(" WHERE ");
        if (idField != null) {
            // 拼接一下條件,這里是id,如果是別的條件,可以使用map傳參或其他實(shí)現(xiàn)方式
            stringBuilder.append(idField.getName() + " = '" + map.get(idField.getName()) + "'");
        }
        Query dataQuery = entityManager.createNativeQuery(stringBuilder.toString());
        dataQuery.executeUpdate();
        return t;
    }

    /**
     * 駝峰 轉(zhuǎn)下劃線
     * @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();
    }
}

使用也是非常簡(jiǎn)單

@Autowired
BaseService<Health> baseService;

然后就baseService.update就OK了。
也可以添加到通用接口中,那么繼承的service就有這個(gè)方法了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容