spring boot 中 crud如何優雅的實現-附代碼

crud編寫的四種方式

1 裸寫crud

待演示

2 extend AbstractCrudController

 待演示

3 spring data rest

 待演示

4 ControllerHelper

重點來了哈哈 直接上代碼

import com.alibaba.fastjson.JSON;
import com.st.cms.common.spring.jpa.AbstractEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.*;

import javax.persistence.EntityNotFoundException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
import static org.springframework.beans.BeanUtils.getPropertyDescriptors;

/**
 * @author yangrd
 * @date 2019/3/1
 **/
@Slf4j

@RestController
public class ControllerHelper implements ApplicationContextAware {

    private MappingManager mappingManager;

    @PostMapping("/{repository}")
    public ResponseEntity create(@PathVariable String repository, @RequestBody String reqJSON) {
        Optional<JpaRepository> jpaRepository = mappingManager.getJpaRepository(repository);
        if (jpaRepository.isPresent()) {
            Object object = mappingManager.getEntityObj(repository);
            if (object instanceof AbstractEntity) {
                ((AbstractEntity) object).setId(UUID.randomUUID().toString());
            }
            Object req = JSON.parseObject(reqJSON, object.getClass());
            BeanUtils.copyProperties(req, object);
            jpaRepository.ifPresent(repo -> repo.saveAndFlush(object));
            return ResponseEntity.status(HttpStatus.CREATED).build();
        }
        return ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{repository}/{id}")
    public ResponseEntity delete(@PathVariable String repository, @PathVariable String id) {
        Optional<JpaRepository> jpaRepository = mappingManager.getJpaRepository(repository);
        if (jpaRepository.isPresent()) {
            jpaRepository.ifPresent(repo -> repo.deleteById(id));
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }

    @PutMapping("/{repository}/{id}")
    public ResponseEntity update(@PathVariable String repository, @PathVariable String id, @RequestBody String reqJSON) {
        Optional<JpaRepository> jpaRepository = mappingManager.getJpaRepository(repository);
        if (jpaRepository.isPresent()) {
            jpaRepository.ifPresent(repo -> repo.findById(id).ifPresent(db -> {
                Object req = JSON.parseObject(reqJSON, db.getClass());
                BeanUtils.copyProperties(req, db);
                repo.saveAndFlush(db);
            }));
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }


    @GetMapping("/{repository}/{id}")
    public ResponseEntity get(@PathVariable String repository, @PathVariable String id) {
        Optional<JpaRepository> jpaRepository = mappingManager.getJpaRepository(repository);
        if (jpaRepository.isPresent()) {
            Optional<Object> optional = jpaRepository.map(repo -> repo.findById(id)).orElseGet(() -> Optional.empty());
            return optional.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
        }
        return ResponseEntity.notFound().build();
    }

    @GetMapping("/{repository}")
    public ResponseEntity get(@PathVariable String repository, Pageable pageable) {
        Optional<JpaRepository> jpaRepository = mappingManager.getJpaRepository(repository);
        if (jpaRepository.isPresent()) {
            return ResponseEntity.ok(jpaRepository.map(repo -> repo.findAll(pageable)).orElse(null));
        }
        return ResponseEntity.notFound().build();
    }

    class MappingManager {
        private Map<String, JpaRepository> entity4Repositories = new HashMap<>();
        private Map<String, Class> entity4Class = new HashMap<>();

        MappingManager(ApplicationContext applicationContext) {
            Map<String, JpaRepository> repositoryBeans = applicationContext.getBeansOfType(JpaRepository.class);
            repositoryBeans.forEach((repositoryName, repositoryBean) -> {
                Class entityClass = MappingSupport.getEntityClass(repositoryBean);
                String entityClassName = MappingSupport.getEntityName(entityClass);
                entity4Repositories.put(entityClassName, repositoryBean);
                entity4Class.put(entityClassName, entityClass);
            });
        }

        public Optional<JpaRepository> getJpaRepository(String repository) {
            return Optional.ofNullable(entity4Repositories.get(repository));
        }

        public Object getEntityObj(String repository) {
            return Optional.ofNullable(entity4Class.get(repository)).map(clazz -> {
                try {
                    return clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                return null;
            }).orElseThrow(EntityNotFoundException::new);
        }
    }

    static class MappingSupport {
        static Class getEntityClass(JpaRepository jpaRepository) {
            Type[] jpaInterfacesTypes = jpaRepository.getClass().getGenericInterfaces();
            Type[] type = ((ParameterizedType) ((Class) jpaInterfacesTypes[0]).getGenericInterfaces()[0]).getActualTypeArguments();
            return (Class) type[0];
        }

        static String getEntityName(Class clazz) {
            String simpleName = clazz.getSimpleName();
            return simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
        }
    }

    /**
     * @author yangrd
     * @date 2018/8/30
     * @see org.springframework.beans.BeanUtils#copyProperties(Object, Object, Class, String...)
     **/
    static class BeanUtils {

        public static <T> T map(Object source, Class<T> targetClass) {
            T targetObj = null;
            try {
                targetObj = targetClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            copyProperties(source, targetObj);
            return targetObj;
        }

        /**
         * 只拷貝不爲null的屬性
         *
         * @param source the source bean
         * @param target the target bean
         * @throws BeansException if the copying failed
         */
        public static void copyProperties(Object source, Object target) throws BeansException {
            copyProperties(source, target, null, (String[]) null);
        }

        /**
         * 只拷貝不爲null的屬性
         *
         * @param source           the source bean
         * @param target           the target bean
         * @param editable         the class (or interface) to restrict property setting to
         * @param ignoreProperties array of property names to ignore
         * @throws BeansException if the copying failed
         * @see BeanWrapper
         */
        private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)
                throws BeansException {

            Assert.notNull(source, "Source must not be null");
            Assert.notNull(target, "Target must not be null");

            Class<?> actualEditable = target.getClass();
            if (editable != null) {
                if (!editable.isInstance(target)) {
                    throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
                }
                actualEditable = editable;
            }
            PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
            List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;

            for (PropertyDescriptor targetPd : targetPds) {
                Method writeMethod = targetPd.getWriteMethod();
                if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
                    PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                    if (sourcePd != null) {
                        Method readMethod = sourcePd.getReadMethod();
                        if (readMethod != null &&
                                ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                            try {
                                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                    readMethod.setAccessible(true);
                                }
                                Object value = readMethod.invoke(source);
                                if (value != null) {
                                    //只拷貝不爲null的屬性 by zhao
                                    if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                        writeMethod.setAccessible(true);
                                    }
                                    writeMethod.invoke(target, value);
                                }
                            } catch (Throwable ex) {
                                throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                            }
                        }
                    }
                }
            }
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Assert.notNull(applicationContext, "");
        this.mappingManager = new MappingManager(applicationContext);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章