美文网首页
Spring JPA 之非全量更新实现

Spring JPA 之非全量更新实现

作者: ConnorG | 来源:发表于2020-03-07 18:46 被阅读0次

    不废话直接上代码

    定义接口

    public interface BaseService<T> {
    
        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<T> baseService;
     * 然后调用.update(T)即可
     * @author Connor
     * 2020-03-09
     * @param <T> 加了@Entity注解的数据库模型
     */
    @Service
    public class BaseServiceImpl<T> implements BaseService<T> {
    
        @Autowired
        EntityManager entityManager;
    
        /**
         * JPA默认更新必须加入事务管理,所以这里的@Transactional注解不能省略掉
         * @param t
         * @return
         */
        @Override
        @Transactional
        public T update(T t) {
            // 先把对象转map,这里会自动过滤null值
            Map<String, Object> map = new BeanMap(t);
            Iterator<String> 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<Health> baseService;
    

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

    相关文章

      网友评论

          本文标题:Spring JPA 之非全量更新实现

          本文链接:https://www.haomeiwen.com/subject/dijhdhtx.html